Skip to content

Sending push notifications from a Node.js server

Send FCM push notifications from Node.js: firebase-admin setup, one user, many tokens, topics, all users, scheduling and cleaning up dead tokens.

PushUpdated 19 October 20267 min read

This is the server side of push in Node.js with the Firebase Admin SDK: one device, one user with many devices, many users, everyone, and keeping your token list clean. It uses the HTTP v1 API, which replaced the legacy API in 2024.

Setup

npm install firebase-admin

Download a service account key from the Firebase console (Project settings, Service accounts) and keep it out of your repo, for example in an environment variable.

import { initializeApp, cert } from "firebase-admin/app";
import { getMessaging } from "firebase-admin/messaging";

initializeApp({ credential: cert(JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT)) });
const fcm = getMessaging();

Send to one device

await fcm.send({
  token: deviceToken,
  notification: { title: "Your order is out for delivery", body: "Ravi is 10 minutes away" },
  data: { order_id: "88213" },        // strings only
  android: { priority: "high" },
});

Send to every device of one user

FCM knows tokens, not users. Store tokens against your user id, then send to all of them.

const tokens = await db.tokens.find({ user_id: "usr_8f21c4" }); // your table

const res = await fcm.sendEachForMulticast({
  tokens,                              // up to 500
  notification: { title: "New login on Chrome", body: "Was this you?" },
});

// Remove tokens FCM says are dead
res.responses.forEach((r, i) => {
  if (r.error?.code === "messaging/registration-token-not-registered") db.tokens.delete(tokens[i]);
});

Send to everyone, or a segment

Use topics. Subscribe when a device registers:

await fcm.subscribeToTopic(tokens, "all");
await fcm.subscribeToTopic(tokens, "city-pune");

Then one call reaches them all:

await fcm.send({ topic: "offers", notification: { title: "Weekend offer", body: "20% off fruit" } });

Scheduling

FCM sends immediately. To send at 6 pm, schedule the call yourself: a job queue such as BullMQ, a cron job, or a cloud scheduler that calls your endpoint. Store the job id so it can be cancelled.

Things that go wrong in production

  • Numbers in data. Every value must be a string, or FCM returns INVALID_ARGUMENT.
  • Tokens never cleaned. Delete on registration-token-not-registered; otherwise lists grow and sends slow down.
  • Sending one by one in a loop. Use sendEachForMulticast or topics, and respect the default quota of 600,000 messages a minute per project.
  • Silent data messages for things users must see. They are not shown when the app is killed on many phones. See why FCM notifications are not received.

The same with SignOps

SignOps Push keeps the token table, topics, scheduling and cleanup for you. With your Firebase and Apple keys uploaded once:

import { SignOps } from "@signops/node";
const signops = new SignOps(process.env.SIGNOPS_KEY);

await signops.push.send({
  user_id: "usr_8f21c4",                     // every device of this user
  title: "Your order is out for delivery",
  body: "Ravi is 10 minutes away",
  send_at: "2026-10-24T18:00:00+05:30",      // optional
});

Sources: Firebase: send messages with the Admin SDK, Firebase: topic messaging.

Frequently asked questions

More questions? Email us and the answer goes on this page.

hello@signops.dev
How do I send a push notification to all users with Firebase?

Subscribe every device to a topic such as all when it registers, then send one message to that topic. HTTP v1 has no single call that reaches every token of a project.

Can I schedule a push notification with FCM?

Not from the API. FCM sends immediately. Schedule from your own server with a job queue or cron, or use the Firebase console's campaign scheduler for one-off messages.

How many tokens can sendEachForMulticast take?

Up to 500 tokens per call. For more, split the list, or use a topic.

Send your first push today.

Free for 1,000 push devices, 100 approvals a day, 10,000 passkey sign-ins a month and 500 email codes to try. No card until you pass a free amount.