Sending Firebase Cloud Messaging notifications from terminal

  • #code
  • #it
  • #problem-solving

Problem

If you are just starting to use Firebase Cloud Messaging, you may wonder if it is possible to send notifications to different combinations of topics, e.g. with condition.

And the answer is yes:

('en' in topics) && ('blog' in topics)

Howerver, currently it is not possible to start campaign in Firebase console with such condition.

Solution

Use Firebase Admin SDK. For example, you can write a simple command line application like this:

import {program} from "commander";  
import {initializeApp} from "firebase-admin/app";  
import {cert} from "firebase-admin/app";  
import Handlebars from "handlebars";  
  
import {getMessaging} from "firebase-admin/messaging";  
import {readFile} from "node:fs/promises";  
import {createInterface} from "node:readline";  
import nestedProperty from "nested-property";  
import chalk from "chalk";  
  
  
  
const rl = createInterface({  
    input: process.stdin,  
    output: process.stdout,  
})  
  
async function q(query) {  
    return await new Promise(resolve => {  
        return rl.question(query, resolve)  
    })  
}  
  
program  
    .version("0.0.1")  
    .description("Command line tool for sending notifications with FCM")  
    .command("send <template_file> <credentials_file>")  
    .description("Send notification")  
    .action(async (template_file, credentials_file) => {  
        // Init app  
        initializeApp({  
            credential: cert(credentials_file)  
        });  
  
        const template = await readFile(template_file, 'utf8')  
  
        const compiledTemplate = Handlebars.compile(template);  
  
        let context = {}  
        // Ask user to enter values for all variables
        for (let variable of template.matchAll(/{{[{]?(.*?)[}]?}}/g)) {  
            variable = variable[1].trim()  
  
            nestedProperty.set(context, variable, await q(chalk.green(`Enter ${chalk.yellow(variable)}: `)))  
        }  
        const jsonValue = compiledTemplate(context);  
  
        console.log(chalk.green("Template with applied values:"))  
        console.log(jsonValue);  
        if ((await q(chalk.yellow("Send? (y/N):"))) === 'y') {  
            await getMessaging().send(JSON.parse(jsonValue));  
            console.log(chalk.green("DONE"));  
            process.exit(0);  
        } else {  
            console.log(chalk.red("Cancelled"));  
        }  
    })  
;  
program.parse(process.argv);

Then, templating can be used to achieve great flexibility:

{  
  "notification": {  
    "title": "Check out new blogpost at captaindno.xyz",  
    "body": "{{ body }}"  
  },  
  "webpush": {  
    "fcmOptions": {  
      "link": "{{ link }}"  
    }  
  },  
  "condition": "('en' in topics) && ('blog' in topics)"  
}

This is just a basic example, you may and even should make a tool that will suit your needs best.

Source code is available on GitHub

Looks like (just maybe) you are enjoying my content. Check out settings to subscribe to push-notifications or allow collections of some analytical data that helps to improve this website and its content.

Go to settings