npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

imapflow

v2.5.2

Published

IMAP Client for Node

Readme

ImapFlow

Modern and easy-to-use IMAP client library for Node.js.

npm license

ImapFlow provides a clean, promise-based API for working with IMAP, so you don't need in-depth knowledge of the protocol. IMAP extensions are detected and handled automatically. You write the same code regardless of server capabilities, and ImapFlow adapts behind the scenes. ImapFlow is the IMAP engine that powers EmailEngine, a self-hosted email API built by the same team.

Features

  • Async/await API - all methods return Promises
  • Automatic IMAP extension handling - CONDSTORE, QRESYNC, IDLE, COMPRESS, and more
  • Message streaming - async iterators for efficient processing
  • Mailbox locking - built-in locking mechanism for safe concurrent access
  • TypeScript support - written in TypeScript, type definitions included
  • ES modules and CommonJS - import { ImapFlow } from 'imapflow' and const { ImapFlow } = require('imapflow') both work
  • Proxy support - SOCKS and HTTP CONNECT proxies
  • Gmail support - labels, raw search via X-GM-EXT-1

Installation

npm install imapflow

ImapFlow requires Node.js 20 or newer. The package ships both an ES module build and a CommonJS build with bundled type declarations, so no separate @types package is needed.

The ES module build also runs on Bun (tested against the latest release) and on Cloudflare Workers with the nodejs_compat compatibility flag. On Workers connect with implicit TLS (secure: true, usually port 993) or in cleartext: the runtime can not upgrade an already connected socket, so a STARTTLS negotiation fails with a TLS error, and it does not allow turning certificate validation off, so tls: { rejectUnauthorized: false } is rejected with ERR_OPTION_NOT_IMPLEMENTED. COMPRESS=DEFLATE, IDLE and the default pino logger work as on Node.js. STARTTLS works on Workers when the connection goes through createSocketsTransport() (see below).

celld, the self-hosted runtime for Cloudflare-style Workers, has no node:net, node:tls or node:dns. Pass the connect() of cloudflare:sockets through the transport option:

import { connect } from 'cloudflare:sockets';
import { ImapFlow, createSocketsTransport } from 'imapflow';

const client = new ImapFlow({
    host: 'imap.example.com',
    port: 993,
    secure: true,
    auth: { user: '[email protected]', pass: 'secret' },
    transport: createSocketsTransport(connect)
});

Implicit TLS, STARTTLS, cleartext, IDLE, download() and the default logger work. Not available there:

  • The runtime verifies every certificate against its own root store, so tls.rejectUnauthorized: false, a custom tls.ca, client certificates and a servername that differs from host are refused with UnsupportedTransportOption. A TLS connection to an IP address needs a certificate issued for that address.
  • COMPRESS=DEFLATE is skipped, the runtime has no streaming zlib.
  • The proxy option can not be combined with a transport.
  • client.tls stays false, the runtime reports no cipher details.
  • A socket is closed when the request (or Durable Object event) that opened it ends. Run the whole session inside the handler, or inside ctx.waitUntil() to keep it going after the response.

Other connections

The transport option takes any object with a connect() method, so other socket APIs plug in the same way. For an API built on web streams (a connection that opens with a promise of a ReadableStream and a WritableStream of bytes, as cloudflare:sockets and Chrome's Direct Sockets do), createStreamsTransport() does the wrapping:

import { ImapFlow, createStreamsTransport } from 'imapflow';

const transport = createStreamsTransport({
    // `secure` says whether ImapFlow wants a TLS connection; throw to refuse what the API can not do
    open: ({ host, port, secure }) => {
        const socket = openSocket(host, port, secure);
        return {
            opened: socket.opened.then(() => ({ readable: socket.readable, writable: socket.writable })),
            close: () => socket.close()
        };
    },
    // optional: start TLS on an open connection after STARTTLS. Without it ImapFlow does not try
    // STARTTLS, and with doSTARTTLS: true the connection fails with err.tlsFailed set
    upgrade: (socket, options) => startTlsSomehow(socket)
});

opened may also report remoteAddress, remotePort, localAddress and localPort, and for a TLS connection tls: { authorized, name, version }, which becomes client.tls. If close() fails while the streams are locked, it is called again with the locks released.

An API built on events instead implements the transport itself: connect(options, onConnect) returns a Node.js Duplex stream of bytes and calls onConnect once the connection is up (or throws to refuse it), and an optional upgrade(socket, options, onSecure) starts TLS. The Duplex also needs setTimeout(ms), emitting timeout after ms without traffic like net.Socket does: it is the watchdog behind the socketTimeout option.

In a browser, ImapFlow runs from the ES module build when the bundler provides what Node.js has and browsers do not:

  • node:stream from readable-stream, node:events from events, and the Buffer global from buffer
  • the process global from process, and setImmediate() and clearImmediate() globals
  • node:crypto with an MD5 createHash() (message ids on servers without OBJECTID), and path with parse() (used by a dependency for attachment file names)
  • empty modules for node:net, node:tls, node:dns and node:zlib (COMPRESS is then skipped)

test/browser/ in the repository has the complete esbuild setup, with the shims, and runs it in headless Chrome.

Quick Example

import { ImapFlow } from 'imapflow';
// or in CommonJS: const { ImapFlow } = require('imapflow');

const client = new ImapFlow({
    host: 'imap.example.com',
    port: 993,
    secure: true,
    auth: {
        user: '[email protected]',
        pass: 'password'
    }
});

const main = async () => {
    await client.connect();

    let lock = await client.getMailboxLock('INBOX');
    try {
        // fetch latest message
        let message = await client.fetchOne(client.mailbox.exists, { source: true });
        console.log(message.source.toString());

        // list subjects for all messages
        for await (let message of client.fetch('1:*', { envelope: true })) {
            console.log(`${message.uid}: ${message.envelope.subject}`);
        }
    } finally {
        // always release the lock
        lock.release();
    }

    await client.logout();
};

main().catch(console.error);

An ImapFlow instance holds a single connection and cannot reconnect. Once the connection has closed, or after connect() has been called once, create a new instance to connect again. Calling connect() a second time on the same instance throws an error with the code InstanceReused.

See the Quick Start guide for more examples, including Gmail, Outlook, and Yahoo configuration.

Documentation

Full documentation is available at imapflow.com.

ImapFlow was built for EmailEngine, a self-hosted email API that turns Gmail, Microsoft 365, and IMAP accounts into REST endpoints, with managed OAuth2 and webhooks for incoming mail. If you need a production email integration rather than an IMAP client, start there.

License

Copyright (c) 2020-2025 Postal Systems OU

Licensed under the MIT license.