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

chat-adapter-gmail

v0.1.0

Published

Gmail adapter for Chat SDK — turn an inbox into a first-class Chat SDK channel via Pub/Sub push.

Readme

chat-adapter-gmail

npm version npm downloads

A Chat SDK adapter for Gmail. It turns a mailbox into a first-class Chat SDK channel: incoming mail arrives as normalized messages via Cloud Pub/Sub push, and your handler can reply — or save drafts — as the connected account through the Gmail API.

A Gmail thread maps to a Chat SDK thread, an email maps to a message, and a reply maps to postMessage. The adapter exposes capabilities only; whether to auto-send a reply or leave a draft for review is your application's decision, not the adapter's.

Installation

chat is a peer dependency — install it alongside the adapter:

npm install chat-adapter-gmail chat

Quick start

import { Chat } from "chat";
import { createGmailAdapter } from "chat-adapter-gmail";

const gmail = createGmailAdapter({
  auth: {
    clientId: process.env.GMAIL_CLIENT_ID!,
    clientSecret: process.env.GMAIL_CLIENT_SECRET!,
    redirectUri: process.env.GMAIL_REDIRECT_URI!,
  },
  topicName: "projects/my-project/topics/gmail-push",
  watchLabelIds: ["Label_123"], // only act on mail with this label
  pushAudience: process.env.GMAIL_PUSH_AUDIENCE,
  pushServiceAccountEmail: process.env.GMAIL_PUSH_SERVICE_ACCOUNT,
  // tokenStore / cursorStore: supply persistent stores in production.
});

const chat = new Chat({ adapters: { gmail } });

// Every incoming email is a DM. Here we DRAFT a reply for review (safest
// default); swap `createDraft` for `thread.post(...)` to actually send as you.
chat.onDirectMessage(async (thread, message) => {
  if (message.author.isMe) return; // never reply to ourselves
  const reply = `Thanks for your email! You wrote:\n\n> ${message.text}`;
  await gmail.createDraft(thread.id, { markdown: reply });
  await thread.subscribe(); // catch follow-ups in this thread
});

// Initialize adapters, then start receiving push notifications.
// Renew the watch at least every 7 days (see Platform setup).
await chat.initialize();
await gmail.startWatch();

Expose the webhook from any HTTP entry point — the adapter takes a standard Request and returns a Response, so it runs unchanged on Node, Next.js, or serverless:

// Next.js App Router — app/api/webhook/gmail/route.ts
import { after } from "next/server";

export async function POST(request: Request) {
  return gmail.handleWebhook(request, { waitUntil: (p) => after(() => p) });
}

Environment variables

createGmailAdapter() falls back to these when the matching option is omitted:

| Variable | Required | Example | | ----------------------------- | -------- | --------------------------------------------- | | GMAIL_CLIENT_ID | Yes | 1234.apps.googleusercontent.com | | GMAIL_CLIENT_SECRET | Yes | GOCSPX-xxxxxxxx | | GMAIL_REDIRECT_URI | Yes | http://localhost:3000/oauth2callback | | GMAIL_PUBSUB_TOPIC | For push | projects/my-project/topics/gmail-push | | GMAIL_WATCH_LABEL_IDS | No | Label_123,Label_456 (comma-separated) | | GMAIL_PUSH_AUDIENCE | No* | https://example.com/api/webhook/gmail | | GMAIL_PUSH_SERVICE_ACCOUNT | No* | [email protected] |

* Strongly recommended in production. When neither is set, the adapter skips Pub/Sub token verification (and logs a warning) — fine for local development, unsafe for a public endpoint.

Configuration reference

| Field | Type | Default | Description | | ------------------------- | -------------- | ------------- | --------------------------------------------------------------------------- | | auth.clientId | string | env | OAuth 2.0 client ID. | | auth.clientSecret | string | env | OAuth 2.0 client secret. | | auth.redirectUri | string | env | Redirect URI registered on the OAuth client. | | topicName | string | env | Fully-qualified Pub/Sub topic Gmail publishes to. Required for startWatch. | | watchLabelIds | string[] | ["INBOX"] | Only surface messages carrying one of these label IDs. | | pushAudience | string | env | Expected OIDC aud claim (your webhook URL). | | pushServiceAccountEmail | string | env | Expected OIDC email claim (the push service account). | | tokenStore | TokenStore | in-memory | Persists the OAuth refresh token. | | cursorStore | CursorStore | in-memory | Persists the Gmail historyId cursor. | | name | string | "gmail" | Adapter name used in thread IDs / routing. |

Production note: the in-memory stores lose state on restart. Implement TokenStore and CursorStore against your database or secrets manager so the refresh token and history cursor survive restarts.

Platform setup

  1. Create OAuth credentials. In the Google Cloud console, enable the Gmail API, configure the OAuth consent screen, and create an OAuth client ID (type Web application). Note the client ID/secret and add your redirect URI.
  2. Create a Pub/Sub topic. Create a topic (e.g. gmail-push) and grant Gmail permission to publish to it:
    gcloud pubsub topics create gmail-push
    gcloud pubsub topics add-iam-policy-binding gmail-push \
      --member="serviceAccount:[email protected]" \
      --role="roles/pubsub.publisher"
  3. Create a push subscription pointing at your webhook, with an OIDC token so the adapter can verify it:
    gcloud pubsub subscriptions create gmail-push-sub \
      --topic=gmail-push \
      --push-endpoint="https://example.com/api/webhook/gmail" \
      --push-auth-service-account="[email protected]"
    Set pushAudience to the push endpoint URL and pushServiceAccountEmail to that service account.
  4. Connect an account. Run the one-time OAuth flow to mint a refresh token, then save it in your TokenStore:
    GMAIL_CLIENT_ID=... GMAIL_CLIENT_SECRET=... \
    GMAIL_REDIRECT_URI=http://localhost:3000/oauth2callback \
    node --experimental-strip-types scripts/auth.ts
  5. (Optional) Scope with a label. Create a Gmail filter that applies a label to the mail you want handled, and pass that label's ID as watchLabelIds.
  6. Start (and renew) the watch. Call startWatch() on boot. Gmail watches expire within 7 days, so call renewWatch() on a daily schedule (cron).

Features

  • ✅ Real-time incoming mail via Cloud Pub/Sub push (handleWebhook).
  • ✅ Pub/Sub OIDC token verification (audience + service-account checks).
  • ✅ Send replies as the connected account, correctly threaded (In-Reply-To / References + Gmail threadId).
  • createDraft() capability for review-before-send workflows.
  • ✅ Label-scoped watching; self-sent mail is filtered to prevent reply loops.
  • ✅ Fetch thread history (fetchMessages / fetchThread), parse plain-text and HTML bodies, strip quoted replies, and surface attachment metadata.
  • ✅ Markdown ⇄ HTML email formatting.

Limitations (email has no equivalent — these throw NotImplementedError or are no-ops): editing sent messages, reactions, typing indicators, modals, ephemeral messages. Outgoing attachments are not yet supported. historyId cursor and OAuth token persistence require stores you provide.

License

MIT