Firebase Topic Messaging
Topic messaging lets you send one notification to many users simultaneously without knowing each user's individual FCM token. Users subscribe to named topics — like "sports-news" or "order-updates" — and you send messages to the topic. Every subscriber receives the notification.
Topics vs Tokens
Sending to individual tokens: - Send to User A's token - Send to User B's token - Send to User C's token = 3 separate API calls, 3 tokens to manage Sending to a topic: - Subscribe A, B, C to topic "daily-deals" - Send one message to topic "daily-deals" = 1 API call, FCM delivers to all subscribers
Topics are ideal for broadcasts — news alerts, promotional announcements, app update notices — where the same message goes to a large group.
Subscribing Users to a Topic
Subscription happens server-side using the Admin SDK (you cannot subscribe from client code):
// Cloud Function or server script
const admin = require("firebase-admin");
admin.initializeApp();
async function subscribeToTopic(tokens, topic) {
const response = await admin.messaging().subscribeToTopic(tokens, topic);
console.log("Subscribed:", response.successCount, "devices");
console.log("Failed:", response.failureCount, "devices");
return response;
}
// Subscribe a user's device when they enable "sports" notifications
const userToken = "user-device-fcm-token";
await subscribeToTopic([userToken], "sports-news");
Unsubscribing from a Topic
async function unsubscribeFromTopic(tokens, topic) {
const response = await admin.messaging().unsubscribeFromTopic(tokens, topic);
console.log("Unsubscribed:", response.successCount, "devices");
}
await unsubscribeFromTopic([userToken], "sports-news");
Sending a Message to a Topic
async function sendTopicNotification(topic, title, body, data = {}) {
const message = {
notification: { title, body },
data,
topic // topic name (no "/topics/" prefix needed for Admin SDK)
};
const messageId = await admin.messaging().send(message);
console.log("Topic message sent:", messageId);
}
// Send to all subscribers of "sports-news"
await sendTopicNotification(
"sports-news",
"Match Alert",
"India vs Australia — Live now!",
{ match_id: "ind-aus-2026", type: "cricket" }
);
Topic Naming Rules
- Use only letters, numbers, hyphens, underscores, and periods
- Maximum 900 characters
- Case-sensitive:
Sports-Newsandsports-newsare different topics - Use consistent lowercase naming:
sports-news,order-updates,promo-alerts
Combining Topics with Conditions
Send to users subscribed to one topic but not another, or to any of multiple topics, using condition expressions:
// Send to users subscribed to BOTH cricket AND india-team
const message = {
notification: {
title: "Team India wins!",
body: "India beat Australia by 50 runs."
},
condition: "'cricket' in topics && 'india-team' in topics"
};
await admin.messaging().send(message);
// Send to users subscribed to cricket OR football
const message2 = {
notification: {
title: "Live Sports Alert",
body: "Multiple matches happening now!"
},
condition: "'cricket' in topics || 'football' in topics"
};
await admin.messaging().send(message2);
Conditions support up to 5 topics combined with AND (&&) and OR (||) operators.
Managing Topic Subscriptions in Your App
A common pattern stores each user's notification preferences in Firestore and syncs them to FCM topics via a Cloud Function:
// When user toggles "Cricket notifications" in app settings
async function updateNotificationPreference(userId, topic, enabled) {
const db = admin.firestore();
await db.collection("users").doc(userId).update({
[`notification_prefs.${topic}`]: enabled
});
// A Firestore trigger function handles subscribing/unsubscribing
}
// Cloud Function triggered by user settings change
exports.syncTopicSubscriptions = onDocumentUpdated(
"users/{userId}",
async (event) => {
const before = event.data.before.data();
const after = event.data.after.data();
const userId = event.params.userId;
// Get user's FCM tokens
const tokensSnap = await admin.firestore()
.collection("users").doc(userId).collection("fcm_tokens").get();
const tokens = tokensSnap.docs.map((d) => d.data().token);
if (tokens.length === 0) return;
// Check which topics changed
const prefsBefore = before.notification_prefs || {};
const prefsAfter = after.notification_prefs || {};
for (const topic of Object.keys(prefsAfter)) {
if (prefsBefore[topic] !== prefsAfter[topic]) {
if (prefsAfter[topic]) {
await admin.messaging().subscribeToTopic(tokens, topic);
} else {
await admin.messaging().unsubscribeFromTopic(tokens, topic);
}
}
}
}
);
Key Takeaway
Topic messaging broadcasts one notification to all subscribers of a named topic with a single API call. Manage subscriptions server-side with the Admin SDK using subscribeToTopic and unsubscribeFromTopic. Use condition expressions to target combinations of topics. Store user notification preferences in Firestore and sync changes to FCM topic subscriptions via Cloud Function triggers.
