Skip to main content
Technology & EngineeringNotification Services171 lines

Firebase Cloud Messaging

Integrate Firebase Cloud Messaging (FCM) for cross-platform push notifications.

Quick Summary26 lines
You are a push notification engineer who integrates Firebase Cloud Messaging into projects. FCM is Google's cross-platform messaging solution for Android, iOS, and web applications. It supports both notification messages displayed by the system tray and data messages handled by the application. FCM handles device token lifecycle, topic-based fanout, and condition-based targeting.

## Key Points

- **Storing tokens in memory only**: Tokens must persist in a database and be refreshed regularly; in-memory maps are lost on redeploy.
- **Ignoring send errors**: Stale tokens accumulate and slow down batch sends; always handle `registration-token-not-registered`.
- **Using notification messages for all cases**: Notification messages bypass your app's rendering logic; use data messages when you need custom behavior.
- **Sending to individual tokens for broadcasts**: Use topics or `sendEach` with batching instead of sequential single sends.
- Cross-platform mobile and web push notification delivery
- Real-time alerts for transactional events like order updates or chat messages
- Broadcast notifications to subscriber segments via topics
- Background data sync triggers on mobile devices
- Apps already using the Firebase ecosystem for auth or Firestore

## Quick Example

```bash
npm install firebase-admin
```

```env
GOOGLE_APPLICATION_CREDENTIALS=./service-account.json
FCM_PROJECT_ID=your-project-id
```
skilldb get notification-services-skills/Firebase Cloud MessagingFull skill: 171 lines
Paste into your CLAUDE.md or agent config

Firebase Cloud Messaging

You are a push notification engineer who integrates Firebase Cloud Messaging into projects. FCM is Google's cross-platform messaging solution for Android, iOS, and web applications. It supports both notification messages displayed by the system tray and data messages handled by the application. FCM handles device token lifecycle, topic-based fanout, and condition-based targeting.

Core Philosophy

Token Lifecycle Management

Device tokens are ephemeral. They rotate when users reinstall the app, clear data, or restore to a new device. Your backend must treat tokens as mutable references, refreshing them on every app launch and pruning stale tokens when FCM returns messaging/registration-token-not-registered. Never cache tokens indefinitely without a refresh strategy.

Data Messages Over Notification Messages

Notification messages are convenient but limit control. The OS renders them, and your app has no say in formatting, grouping, or conditional suppression. Data messages give full control to your application code, allowing custom rendering, local deduplication, and analytics hooks before display. Default to data messages unless you need background delivery without a service worker.

Topic Fanout for Scale

Sending to individual tokens works for transactional notifications. For broadcast or segment-based delivery, use topics. Topics let FCM handle fanout server-side, removing the need to batch thousands of individual sends. Combine topics with conditions for boolean targeting like 'sports' in topics && !('baseball' in topics).

Setup

Install

npm install firebase-admin

Environment Variables

GOOGLE_APPLICATION_CREDENTIALS=./service-account.json
FCM_PROJECT_ID=your-project-id

Key Patterns

1. Send Data Message

Do:

import { getMessaging } from "firebase-admin/messaging";

const message = {
  data: { type: "order_update", orderId: "abc123", status: "shipped" },
  token: deviceToken,
  android: { priority: "high" as const },
  apns: { payload: { aps: { contentAvailable: true } } },
};

const response = await getMessaging().send(message);

Not this:

// Notification messages remove client-side control
const message = {
  notification: { title: "Order Update", body: "Your order shipped!" },
  token: deviceToken,
};

2. Token Refresh Handling

Do:

import { getFirestore, FieldValue } from "firebase-admin/firestore";

async function saveToken(userId: string, token: string): Promise<void> {
  const db = getFirestore();
  await db.collection("users").doc(userId).update({
    fcmTokens: FieldValue.arrayUnion(token),
    tokenUpdatedAt: FieldValue.serverTimestamp(),
  });
}

async function removeStaleToken(userId: string, token: string): Promise<void> {
  const db = getFirestore();
  await db.collection("users").doc(userId).update({
    fcmTokens: FieldValue.arrayRemove(token),
  });
}

Not this:

// Storing a single token that never gets updated
const tokenMap: Record<string, string> = {};
tokenMap[userId] = token; // Lost on restart, never refreshed

3. Topic-Based Broadcasting

Do:

import { getMessaging } from "firebase-admin/messaging";

await getMessaging().subscribeToTopic([deviceToken], "breaking-news");

const result = await getMessaging().send({
  topic: "breaking-news",
  data: { headline: "Major event", articleId: "xyz" },
});

Not this:

// Manually iterating all tokens for a broadcast
const allTokens = await getAllUserTokens();
for (const token of allTokens) {
  await getMessaging().send({ token, data: payload }); // Slow, rate-limited
}

Common Patterns

Batch Sending with Error Handling

import { getMessaging } from "firebase-admin/messaging";

const messages = tokens.map((token) => ({
  token,
  data: { event: "price_alert", symbol: "AAPL" },
}));

const response = await getMessaging().sendEach(messages);
response.responses.forEach((res, idx) => {
  if (res.error?.code === "messaging/registration-token-not-registered") {
    removeStaleToken(userIds[idx], tokens[idx]);
  }
});

Conditional Topic Targeting

await getMessaging().send({
  condition: "'stock-alerts' in topics && 'tech-sector' in topics",
  data: { symbol: "AAPL", change: "+2.5%" },
});

Platform-Specific Configuration

const message = {
  data: { type: "chat" },
  token: deviceToken,
  android: { ttl: 3600 * 1000, priority: "high" as const },
  apns: {
    headers: { "apns-priority": "10", "apns-expiration": "0" },
    payload: { aps: { sound: "default", badge: 1 } },
  },
  webpush: { headers: { Urgency: "high" } },
};

Anti-Patterns

  • Storing tokens in memory only: Tokens must persist in a database and be refreshed regularly; in-memory maps are lost on redeploy.
  • Ignoring send errors: Stale tokens accumulate and slow down batch sends; always handle registration-token-not-registered.
  • Using notification messages for all cases: Notification messages bypass your app's rendering logic; use data messages when you need custom behavior.
  • Sending to individual tokens for broadcasts: Use topics or sendEach with batching instead of sequential single sends.

When to Use

  • Cross-platform mobile and web push notification delivery
  • Real-time alerts for transactional events like order updates or chat messages
  • Broadcast notifications to subscriber segments via topics
  • Background data sync triggers on mobile devices
  • Apps already using the Firebase ecosystem for auth or Firestore

Install this skill directly: skilldb add notification-services-skills

Get CLI access →