Skip to content

FCM Flow

  1. Add firebase to your android project.
  2. Create a firebase project
  3. Register your app with Firebase
  • Go to the Firebase console.

    • In the center of the project overview page, click the Android icon or Add app to launch the setup workflow.
    • Enter your app’s package name (com.ankurplus.nursery) in the Android package name field.

    Make sure to enter the package name that your app is actually using. The package name value is case-sensitive, and it cannot be changed for this Firebase Android app after it’s registered with your Firebase project.

    • (Optional) Enter an App nickname, which is an internal, convenience identifier that is only visible to you in the Firebase console.
    • Click Register app.
  1. Add a firebase configuration file

    • Download the google-services.json file from the Firebase console.
    • Place the file in the app module’s directory. (Cordova project’s root directory)
  2. For cordova - Use config.xml and cordova-plugin-firebasex to set up Firebase using google-services.json file.

  • Endpoint: POST /register-token
  • Payload:
{
"token": "AAA.BBB.CCC"
}

Node.js validates and stores tokens in the database. Respond with:

{
"success": true
}
  • Endpoint: POST /send-global-notification
  • Payload:
{
"title": "New Offer",
"body": "Flat 50% off today!",
"imageUrl": "https://your-server.com/banners/offer50.png"
}
  • Response:
{
"success": true
}
const message = {
notification: {
title: req.body.title,
body: req.body.body,
image: req.body.imageUrl,
},
tokens: allTokensFromDB,
};
// firebase sdk methods
const result = await admin.messaging().sendEachForMulticast(message);
  • Android automatically displays title + body + big image (if provided) when the notification payload is used.
  • We cannot style this in cordova - Because Android OS shows the notification before your app gets control.

Alternative: Node Sends Data Only - We can style as we like

Section titled “Alternative: Node Sends Data Only - We can style as we like”

When you need Cordova to fully control the notification UI (dynamic images, custom layouts):

const message = {
token,
data: {
title: "New Offer",
body: "Flat 50% off today!",
type: "OFFER",
},
};
document.addEventListener(
"deviceready",
function () {
console.log("Device is ready");
// Listen for FCM messages
FirebasePlugin.onMessageReceived(
function (msg) {
console.log("Message received from FCM:", msg);
// 1. Normalize data
// - For data-only messages, `msg.data` will have what backend sent
var data = msg.data || msg || {};
var title = data.title || "Notification";
var body = data.body || "";
// 2. Decide styling based on data from backend
// Example: choose emoji & default image based on type
var emoji = "";
var defaultImage = null;
switch (data.type) {
case "SALE":
emoji = " 🛍️";
defaultImage = "https://your-server.com/banners/default-sale.png";
break;
case "ALERT":
emoji = " ⚠️";
defaultImage = "https://your-server.com/banners/alert.png";
break;
default:
emoji = "";
defaultImage = null;
}
// Prefer backend imageUrl, else fallback to default based on type
var bigImageUrl = data.imageUrl || defaultImage;
// Accent color (from backend or fallback)
var color = data.color || "#4CAF50";
// 3. Build local notification options
var notificationOptions = {
id: Date.now(), // unique id
title: title + emoji, // add emoji here
body: body,
smallIcon: "notification_icon", // must exist in your resources
iconColor: color,
vibration: true,
sound: "default",
foreground: true, // show even if app is in foreground
};
// If you want big picture (banner / map snapshot style)
if (bigImageUrl) {
notificationOptions.bigPicture = bigImageUrl; // Firebasex supports this
}
// 4. Show styled local notification
FirebasePlugin.scheduleLocalNotification(
notificationOptions,
function () {
console.log("Local notification shown with styling");
},
function (err) {
console.error("Error showing local notification", err);
},
);
},
function (error) {
console.error("Error in onMessageReceived", error);
},
);
},
false,
);

This approach lets the device decide which artwork, sounds, or actions to attach.


FCM distinguishes two payload types:

"notification": {
"title": "Order shipped",
"body": "Your order #123 is on the way!",
"image": "https://example.com/order_map.png",
"android_channel_id": "my_channel",
"icon": "stock_ticker_update",
"color": "#4CAF50",
"tag": "group_tag",
"click_action": "OPEN_SCREEN",
"sound": "default",
"title_loc_key": "TITLE_STRING",
"body_loc_key": "BODY_STRING",
"body_loc_args": ["arg1"]
}

Field names such as title and body are fixed; renaming them prevents Android from rendering the system notification automatically.

"data": {
"type": "ORDER_UPDATE",
"orderId": "123",
"priority": "high",
"imageUrl": "https://example.com/promo.png",
"foo": "bar",
"whatever": "you want"
}
  • Accepts any string key/value pairs.
  • Cordova receives values exactly as sent.
  • Use it to drive custom logic (deep links, themes, in-app banners).

{
"message": {
"token": "DEVICE_TOKEN",
"notification": {
"title": "Order shipped",
"body": "Your order #123 is on the way!",
"image": "https://example.com/order_map.png",
"sound": "default",
"color": "#4CAF50"
},
"data": {
"orderId": "123",
"type": "ORDER_UPDATE",
"banner": "https://example.com/banner.png",
"screen": "order_details",
"custom1": "value1",
"custom2": "value2"
},
"android": {
"priority": "HIGH"
}
}
}

  • Use notification payload for immediate system UI (requires title + body).
  • Use data payload for unlimited custom fields and local notification rendering.
  • Cordova can mix both: show system notification when app is backgrounded, and raise a customized local notification when foregrounded.
  • Always keep google-services.json, Gradle plugins, and Firebase BoM versions in sync across environments.

Is it necessary to store tokens in a database?

Section titled “Is it necessary to store tokens in a database?”

Storing tokens in a database is only required when you need to target specific devices or user segments. Decide based on the type of notification:

Use caseStore tokens?Recommended approach
Global broadcast (same message to all)NoFCM Topics
User/device-specific alertsYesToken database
Segmented audiences (VIP, region)OftenToken DB + metadata or multiple topics

If you only send global announcements (offers, maintenance windows, feature launches), skip token storage and rely on topics:

Node.js
// Cordova
FirebasePlugin.subscribe("global_notifications");
await admin.messaging().send({
topic: "global_notifications",
notification: {
title: "New Offer",
body: "Big discounts today!",
},
});

Every device subscribed to global_notifications receives the message.

  • No additional DB storage on your side. When a device calls FirebasePlugin.subscribe(“global_notifications”), FCM records internally that the token belongs to that topic. Firebase handles that mapping; your backend just targets the topic name.

For per-user updates (orders, rides, reminders) the backend must know which token belongs to whom:

  1. Cordova posts the token to the backend.
  2. Backend stores the token (optionally linked to user/device metadata).
  3. Backend queries the token(s) and calls sendEachForMulticast or sendToDevice.

Without persistence the backend cannot target individual recipients.


Create a backend/app contract: include navigation hints in the payload so the client can open the right screen.

{
"message": {
"topic": "global",
"data": {
"title": "Order placed",
"body": "Order #123 has been created",
"screen": "order_details",
"orderId": "123"
}
}
}

On Cordova, centralize the routing logic:

function handleNotificationNavigation(data) {
switch (data.screen) {
case "order_details":
openOrderDetails(data.orderId);
break;
case "offers":
openOffersPage();
break;
default:
openHome();
break;
}
}

Detecting taps with cordova-plugin-firebasex

Section titled “Detecting taps with cordova-plugin-firebasex”

FirebasePlugin.onMessageReceived provides a tap flag:

  • tap: "background" → user tapped while app was backgrounded or closed.
  • tap: "foreground" → user tapped a foreground notification.
  • tap undefined → message just arrived; no tap yet.
document.addEventListener(
"deviceready",
function () {
FirebasePlugin.onMessageReceived(
function (message) {
const data = message.data || {};
const tap = message.tap;
if (tap) {
handleNotificationNavigation(data);
return;
}
// Message received while app is active; decide whether to show a local notification or banner.
},
function (err) {
console.error("onMessageReceived error:", err);
}
);
},
false
);

Android’s native FCM notification payload has limited support for multiple actions. For richer CTAs, send a data-only push and render your own local notification using cordova-plugin-local-notifications (or similar):

cordova.plugins.notification.local.schedule({
id: Date.now(),
title: data.title,
text: data.body,
actions: [
{ id: "VIEW_ORDER", title: "View" },
{ id: "CANCEL_ORDER", title: "Cancel" },
],
data: {
screen: "order_details",
orderId: data.orderId,
},
});

Handle the action callback to inspect actionId and data, then call handleNotificationNavigation or trigger other flows (cancel order, mark read, etc.).

Start with simple tap-to-open flows; layer CTAs later when you need richer interactions.