How can scheduled Firebase Cloud Messaging notifications be made outside of the Firebase Console?
W

2

18

Inside the Firebase Console, under the Cloud Messaging view, users are able to create test notifications. This functionality also allows you to schedule the time at which the notification will send to a device or set of devices.

Is it possible to create and send scheduled FCM notifications to specific devices by using firebase cloud functions and the Firebase Admin SDK? Is there an alternative way to solving this?

The current way that I send scheduled messages to users is like so:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const schedule = require('node-schedule');
admin.initializeApp();

exports.setScheduledNotification = functions.https.onRequest(async (req, res) => {
    const key = req.query.notification_key;

    const message = {
        notification: {
            title: 'Test Notification',
            body: 'Test Notification body.'
        }
    };

    var currentDate = new Date();
    var laterDate = new Date(currentDate.getTime() + (1 * 60000));

    var job = schedule.scheduleJob(key, laterDate, () => {
        const snapshot = admin.messaging().sendToDevice(key, message);
    });

    return res.status(200).send(`Message has been scheduled.`);
});

First of all, I am unsure how node-schedule interacts with firebase cloud functions. The logs appear that the function terminates very quickly which I would think would be correct. The longer the operation runs the more costly it is in our firebase bills. The notification does still run on a scheduled time though. I'm confused on how that all is working behind the scenes.

Secondly, I am having issues canceling these scheduled notifications. The notifications will most likely be on a 2hr timed schedule from the point it gets created. Before the 2hrs is up, I'd like the have the ability to cancel/overwrite the notification with an updated scheduled time.

I tried this to cancel the notification and it failed to find the previously created notification. Here is the code for that:

exports.cancelScheduledNotification = functions.https.onRequest(async (req, res) => {
    const key = req.query.notification_key;

    var job = schedule.scheduledJobs[key];
    job.cancel();

    return res.status(200).send(`Message has been canceled.`);
});

Is it possible to tap into the scheduling functionality of firebase cloud messaging outside of the firebase console? Or am I stuck with hacking my way around this issue?

Warrenne answered 16/3, 2020 at 21:51 Comment(1)
"Is it possible to create and send scheduled FCM notifications to specific devices by using firebase cloud functions and the Firebase Admin SDK?" Firebase Cloud Message doesn't have an API that allows for scheduled delivery of message. It seems you already know this, as otherwise I'm a bit surprised at the way you're trying to solve the problem. I'll write a longer answer below, but wanted to start with that, as your question is lacking focus that I'd prefer answer to not lack.Fordone
F
23

A Cloud Function can run for a maximum of 9 minutes. So unless you're using node-schedule for periods shorter than that, your current approach won't work. Even if it would work, or if you are scheduling for less than 9 minutes in advance, using this approach is very uneconomic as you'll be paying for the Cloud Functions for all this time while it's waiting.


A more common approach is to store information about what message you want to be delivered to whom at what time in a database, and then use regular scheduled functions to periodically check what messages to send. For more on this, see these previous questions:


A recent improvement on this is to use the Cloud Tasks API to programmatically schedule Cloud Functions to be called at a specific time with a specific payload, and then use that to send the message through FCM. Doug Stevenson wrote a great blog post about this here: How to schedule a Cloud Function to run in the future with Cloud Tasks (to build a Firestore document TTL). While the post is about deleting documents at a certain time, you can combine it with the previous approach to schedule FCM messages too.

Scheduling of tasks is now also described in the documentation on enqueueing functions with Cloud Tasks


A final option, and one I'd actually recommend nowadays, is to separate the delivery of the message from the display of the notification.

Display of data messages (unlike notification messages) is never handled by the system, and always left to your application. So you can deliver the FCM data message straight away that then contains the time to display the message, and then wake the device up to display the message (often called a local notification) at that time.

Fordone answered 16/3, 2020 at 22:27 Comment(6)
I ended up utilizing the Cloud Tasks API to schedule the Cloud Functions. I appreciate the help!Warrenne
@TristanHeilman, can you share more about this? Perhaps some example on how to trigger FCM with Cloud Tasks API?Goldoni
Out of curiosity, if FCM does not support scheduled notifications, how does the Firebase Console solve the problem of scheduled notifications.Ghazi
The FCM API doesn't support scheduled messages. The Firebase console uses its own proprietary mechanism for scheduling notifications.Fordone
@FrankvanPuffelen is it not possible to do this by setting an event_time property?Francie
No, the FCM API doesn't support scheduled messages.Fordone
F
3

To make Frank's answer more tangible, I am including some sample code below for scheduled cloud functions, that can help you achieve the 'scheduled FCM notifications'.

You should store the information required to send your notification(s) in Firestore (e.g. the when-to-notify parameter and the FCM token(s) of the users you want to send the notification to) and run a cloud function every minute to evaluate if there is any notification that needs to be delivered.

The function checks what Firestore documents have a WhenToNofity parameter that is due, and send the notifications to the receiver tokens immediately. Once sent, the function sets the boolean 'notificationSent' to true, to avoid that the users receive the same notification again on the next iteration.

The code below achieves just that:

const admin = require('firebase-admin');
admin.initializeApp();
const database = admin.firestore();

exports.sendNotification = functions.pubsub.schedule('* * * * *').onRun(async (context) => {
    //check whether notification should be sent
    //send it if yes

    const query = await database.collection("experiences")
        .where("whenToNotify", '<=', admin.firestore.Timestamp.now())
        .where("notificationSent", "==", false).get();

    query.forEach(async snapshot => {
        sendNotification(snapshot.data().tokens);
        await database.doc('experiences/' + snapshot.id).update({
            "notificationSent": true,
        });
    });

    function sendNotification(tokens) {

        let title = "INSERT YOUR TITLE";
        let body = "INSERT YOUR BODY";

        const message = {
            notification: { title: title, body: body},
            tokens: tokens,
                    android: {
                        notification: {
                            sound: "default"
                        }
                    },
                    apns: {
                        payload: {
                            aps: {
                                sound: "default"
                            }
                        }
                    }
        };
        admin.messaging().sendMulticast(message).then(response => {
            return console.log("Successful Message Sent");
        }).catch(error => {
            console.log(error);
            return console.log("Error Sending Message");
        });
    }
    return console.log('End Of Function');
});

If you're unfamiliar with setting up cloud functions, you can check how to set them up here. Cloud functions require a billing account, but you get 1M cloud invocations per month for free, which is more than enough to cover the costs of this approach.

Once done, you can insert your function in the index.js file.

Francie answered 15/8, 2022 at 16:34 Comment(1)
how many notifications can you send maximum on each schedule job? if there are too many and you need several jobs, would messages be too late?Tuberose

© 2022 - 2024 — McMap. All rights reserved.