🚀 Migrating to Firebase Topic Messaging for Scalable Chat Notifications

A practical guide to replacing legacy Firebase token-based messaging with topic-based messaging
🔥 The Problem
When building a real-time chat system, push notifications are crucial. Initially, it’s common to send messages to multiple device tokens using Firebase’s legacy API.
However, Firebase has deprecated this functionality — it no longer allows sending messages to multiple device tokens in a single API call.
This introduces a problem:
What if your system has thousands of users? Looping through tokens to send messages one by one isn’t efficient — it consumes memory and slows down delivery.
💡 The Solution: Firebase Topic Messaging
A great alternative is to use Firebase Topic Messaging.
In many chat systems, users participate in channels or rooms. These channels map perfectly to topics in Firebase.
You subscribe a device to a topic (channel).
When a message is sent to that topic, all subscribed devices receive it.
No need to manually manage and loop over tokens.
It's scalable and aligns with how group chat features work.
✅ Requirements
Here’s what you need:
A Firebase project
A service account JSON file
Node.js and the
firebase-adminpackageA socket-based system (like Socket.IO) to track user activity
🔧 Step-by-Step Setup
1. 🔐 Get Firebase Service Account
Go to Firebase Console
Project settings > Service accounts
Click “Generate new private key”
Save the file in a local
.firebase/folder

2. 🛡️ Keep Credentials Secure
Add the folder to your .gitignore:
# .gitignore
.firebase/
3. 📦 Initialize Firebase Admin SDK
Install the package:
npm install firebase-admin
Use environment variables and initialize Firebase:
# .env
FIREBASE_CREDENTIAL_PATH=.firebase/firebase-adminsdk.json
// firebase.js
const admin = require("firebase-admin");
const path = require("path");
const serviceAccount = require(path.resolve(process.env.FIREBASE_CREDENTIAL_PATH));
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
module.exports = admin;
🔄 Topic Subscription Logic
When a user connects via socket and joins channels, subscribe their device token to the corresponding topics:
const subscribeToChannels = async (deviceToken, channelIds) => {
for (const channelId of channelIds) {
await admin.messaging().subscribeToTopic(deviceToken, channelId);
}
};
When the user logs out or disconnects:
const unsubscribeFromChannels = async (deviceToken, channelIds) => {
for (const channelId of channelIds) {
await admin.messaging().unsubscribeFromTopic(deviceToken, channelId);
}
};
📩 Sending a Message to a Topic
Here’s how to broadcast a message to all users in a channel:
const sendToTopic = async (channelId, messageText) => {
const message = {
notification: {
title: "New Message",
body: messageText,
},
topic: channelId,
};
await admin.messaging().send(message);
};
This sends the message to all devices subscribed to the topic.
🧠 Why This Works
It’s scalable: One message to many users.
It’s clean: No need to track tokens manually.
It’s flexible: You can dynamically subscribe/unsubscribe users as they join or leave channels.
📌 Final Thoughts
Using Firebase Topic Messaging helped us scale and simplify notification delivery in a chat system architecture. If you're still using the old token-based multi-send method, now’s a good time to switch.
This approach works especially well if:
You have group-based features (like chat rooms, teams, or event channels)
You want to reduce memory usage and API calls
🗨️ Want to Try It Yourself?
Give it a shot! If you're working on a chat app or anything with groups or channels, topic messaging is worth exploring.
