24
votes

I am trying to make a cloud function that sends a push notification to a given user.

The user makes some changes and the data is added/updated under a node in firebase database (The node represents an user id). Here i want to trigger a function that sends a push notification to the user.

I have the following structure for the users in DB.

Users

 - UID
 - - email
 - - token

 - UID
 - - email
 - - token

Until now i have this function:

exports.sendNewTripNotification = functions.database.ref('/{uid}/shared_trips/').onWrite(event=>{
const uuid = event.params.uid;

console.log('User to send notification', uuid);

var ref = admin.database().ref('Users/{uuid}');
ref.on("value", function(snapshot){
        console.log("Val = " + snapshot.val());
        },
    function (errorObject) {
        console.log("The read failed: " + errorObject.code);
});

When i get the callback, the snapshot.val() returns null. Any idea how to solve this? And maybe how to send the push notification afterwards?

5
Does the console.log of the uuid show the correct value? - Jen Person
Yes, the uuid is correct. - Tudor Lozba
Use back-ticks to substitute the value of uuid in your ref: admin.database().ref(`Users/${uuid}`). Also you should use once() instead of on(). on() leaves the listener attached; not something you want in a cloud function. - Bob Snyder
Also, your function should return a promise that indicates to Cloud Functions when your work is done and it's safe to clean up. If you do async work without returning a promise, it will not work the way you want. It might be a good idea to go over these video tutorials to learn more: youtube.com/playlist?list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM - Doug Stevenson
Doug is correct - with Cloud Functions you should be returning a promise so that it can watch the chain execute and then stop the instance when it is done. Because it has no way of knowing that async work is happening in this case, the function is completing before the async work is actually completed. - Andrew Breen

5 Answers

37
votes

I managed to make this work. Here is the code that sends a notification using Cloud Functions that worked for me.

exports.sendNewTripNotification = functions.database.ref('/{uid}/shared_trips/').onWrite(event=>{
    const uuid = event.params.uid;

    console.log('User to send notification', uuid);

    var ref = admin.database().ref(`Users/${uuid}/token`);
    return ref.once("value", function(snapshot){
         const payload = {
              notification: {
                  title: 'You have been invited to a trip.',
                  body: 'Tap here to check it out!'
              }
         };

         admin.messaging().sendToDevice(snapshot.val(), payload)

    }, function (errorObject) {
        console.log("The read failed: " + errorObject.code);
    });
})
9
votes

Just answering the question from Jerin A Mathews... Send message using Topics:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

//Now we're going to create a function that listens to when a 'Notifications' node changes and send a notificcation
//to all devices subscribed to a topic

exports.sendNotification = functions.database.ref("Notifications/{uid}")
.onWrite(event => {
    //This will be the notification model that we push to firebase
    var request = event.data.val();

    var payload = {
        data:{
          username: request.username,
          imageUrl: request.imageUrl,
          email: request.email,
          uid: request.uid,
          text: request.text
        }
    };

    //The topic variable can be anything from a username, to a uid
    //I find this approach much better than using the refresh token
    //as you can subscribe to someone's phone number, username, or some other unique identifier
    //to communicate between

    //Now let's move onto the code, but before that, let's push this to firebase

    admin.messaging().sendToTopic(request.topic, payload)
    .then((response) => {
        console.log("Successfully sent message: ", response);
        return true;
    })
    .catch((error) => {
        console.log("Error sending message: ", error);
        return false;
    })
})
//And this is it for building notifications to multiple devices from or to one.
1
votes

Return this function call.

return ref.on("value", function(snapshot){
        console.log("Val = " + snapshot.val());
        },
    function (errorObject) {
        console.log("The read failed: " + errorObject.code);
});

This will keep the cloud function alive until the request is complete. Learn more about returning promises form the link give by Doug in the comment.

0
votes
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);


exports.sendNotificationToTopic = 
functions.firestore.document('Users/{uuid}').onWrite(async (event) => {

//let title = event.after.get('item_name');
//let content = event.after.get('cust_name');
var message = {
    notification: {
        title: "TGC - New Order Recieved",
        body: "A New Order Recieved on TGC App",
    },
    topic: 'orders_comming',
};

let response = await admin.messaging().send(message);
console.log(response);
});

For sending notifications to a topic, The above code works well for me, if you have any doubt, let me know.

0
votes

Send Notification for a Topic In Cloud function

Topics a basically groups you can send notification for the selected group

    var topic = 'NOTIFICATION_TOPIC';
    const payload = {
        notification: {
            title: 'Send through Topic',
            body: 'Tap here to check it out!'
        }
   };

   admin.messaging().sendToTopic(topic,payload);

You can register the device for any new or existing topic from mobile side