Key Takeaway
The interesting lesson wasn’t simply how to integrate FCM. It was about understanding that a cloud messaging/notification system should be treated as an ongoing lifecycle/syncronisation process rather than a one-time registration event: devices register, registrations can change, backend state can become stale, and the application needs a way to keep that state synchronised. That small realisation shaped an important part of how we built our notification system.
Introduction
When we publish a new article or post a blog on our website, users of our Android app can receive a notification almost immediately. We underlined that our backend detects the publication event, which starts our push-notification pipeline.
Behind that simple experience is a communication pipeline connecting our Android app to the backend server and also forming a handshake between our website, the backend server, and Firebase Cloud Messaging (FCM).
So, the first question we investigated was:
What exactly triggers the notification?
To explore how to answer this question, we decoupled the FCM push notification pipeline into two sequential phases to manage device connectivity and message delivery.
Phase 1: Token Ingestion & Registration- It contains the initial client setup, where the Android Mobile App requests a unique registration token from Google FCM Servers upon Software Development Kit (SDK) initialisation and transmits it to the Backend Server via the defined endpoint at /register-device to be persisted in the database.
Phase 2: Broadcast & Delivery – This phase manages the dissemination of messages, starting with an administrative command from our website and admin panel that prompts the Backend Server to query active tokens from the Database, package and forward the JSON notification payloads to Google FCM Servers in batches, and ultimately push the messages downstream to the targeted active devices.
Conceptually, the flow is illustrated in the diagram below:

Technical Narrative: The Notification Pipeline
Phase 1: Token Generation & Ingestion (Handshake)
i. Google’s FCM Handshake: When a user installs our Android app and opens it for the first time, it asks the user for notification permission, and if granted, the integrated Firebase client SDK establishes an encrypted background connection to Google’s FCM services.
ii. Token Generation: Google provides a unique, secure cryptographic FCM registration token mapped specifically to that device and app instance, and returns it to the client SDK.
iii. Ingestion Request: The Android app captures this token via the onNewToken listener and fires an HTTPS POST request to the backend at the following endpoint: /register-device.
iv. Database Persistence: The backend server validates the payload and writes it directly to the database using an upsert statement (ON DUPLICATE KEY UPDATE), mapping the user to their current device address.
Phase 2: Broadcast (Trigger & Batch Delivery)
When a post is published, it fires a secure background HTTPS POST request to the backend server’s broadcast endpoint at:
POST /v1/broadcast/notify
This request includes the x-api-key header to prove it is a trusted internal service. So our backend server quickly verifies it before processing the request it received from our website frontend.
Conceptually:
Client/website
|
| HTTPS POST
| x-api-key: abcd1234wxyz
↓
Backend Server
|
| Check API key
↓
Valid? ── Yes → Process request
└─ No → Reject request (e.g. Error 401/403)
Payload
The notification details in the JSON body:
json
{
"title": "New Post Published!",
"body": "Title of the Post/Article/Blog.",
"data": { "url": "https://symphonyofknowledge.com/posts/new-post-id" }
}
Step 2: Querying the Database (Backend Server $\leftrightarrow$ Database)
After our backend server (Node.js/Express) has performed the verification check and found that the API key matches. Once authorised, it reaches out to the database, retrieving stored FCM tokens.
Here is the snippet:
javascript
const [rows] = await db.query('SELECT fcm_token FROM device_tokens');
const tokens = rows.map(row => row.fcm_token);
The Result: The server now has a clean JavaScript array of all active target device tokens (e.g., [‘token1’, ‘token2’, ‘token3’]).
Step 3: Handing off to Google (Backend Server $\rightarrow$ FCM)
Now that the server has both the message payload (from Step 1) and the target tokens (from Step 2), it packages them together and hands them over to Firebase Cloud Messaging using the initialised Firebase Admin SDK:
javascript
const response = await admin.messaging().sendEachForMulticast({
tokens,
notification: { title, body },
data: data
});
So, at this point, we can answer one question that became particularly important during the development:
How does a message originating from our server eventually reach a particular device without our server knowing the device’s network address?
Immediately after our server delivers to FCM, it has to determine how to route the message toward the intended device(s). So what FCM does: Google’s FCM servers take over here. They handle the heavy lifting of maintaining open connections to millions of Android devices worldwide, waking up the target devices, and delivering the notification.
Persistent Messaging: FCM Token Registration Lifecycle
According to Firebase documents, an FCM registration token should not be treated as a permanent identity because the token can change due to many reasons, including:
- When an app is reinstalled,
- Restored to another device,
- Its data is cleared.
To ensure our posts and articles always reach their target Android devices, we implemented a simple but important strategy: whenever the application becomes active, when users background the app, and foreground it, we make sure the device’s current messaging registration is synchronised with our backend.
Conceptually:
When the App becomes active
↓
Check/register messaging identity
↓
When the user brings the app to the background (nothing happens)
↓
When the user foregrounds the app
↓
Check/register messaging identity
Send current registration token to backend
↓
Our Backend updates its record
↓
Device remains part of the notification pipeline
This means we don’t simply assume that because a device is registered once, it will remain registered forever.
If Google’s FCM servers find that a token is no longer valid, for instance, let’s say a user uninstalled our app, this will return an error. The backend identifies these as expired tokens and automatically deletes them from the database, keeping our storage clean and the database fast.
For us, the interesting lesson wasn’t simply how to integrate FCM. It was about understanding that messaging is a lifecycle/syncronisation process: devices register, registrations can change, backend state can become stale, and the application needs a way to keep that state synchronised. That small realisation shaped an important part of how we built our notification system.
As we continue to iterate, our research and discovery will try to answer these questions experimentally:
- Where does the FCM registration token come from?
- What happens when the app is reinstalled?
- How does the backend know which token belongs to which installation/user?
- What happens when the phone is offline: does FCM queue the message?
- What happens if delivery fails?
- What is the difference between notification and data messages?
- How does FCM target one device versus many devices?
- What does “delivery” actually mean?
- Where is authentication enforced between our backend and FCM?
- What security prevents an arbitrary server from sending messages to your app’s users?
Reference
https://firebase.google.com/docs/cloud-messaging/fcm-architecture
Share this:


Leave a Reply