0
votes

I'm trying to get push notifications to work using this tutorial: https://www.youtube.com/watch?v=z27IroVNFLI

It's obviously a bit old, but there aren't many good alternatives for Firebase web apps.

I'm getting the following error when my cloud function runs:

fcmSend

Error: Reference.child failed: First argument was an invalid path = "/fcmTokens/[object Object]". Paths must be non-empty strings and can't contain ".", "#", "$", "[", or "]" at validatePathString (/srv/node_modules/@firebase/database/dist/index.node.cjs.js:1636:15) at validateRootPathString (/srv/node_modules/@firebase/database/dist/index.node.cjs.js:1647:5) at Reference.child (/srv/node_modules/@firebase/database/dist/index.node.cjs.js:13688:17) at Database.ref (/srv/node_modules/@firebase/database/dist/index.node.cjs.js:14862:48) at exports.fcmSend.functions.database.ref.onWrite (/srv/index.js:21:12) at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23) at /worker/worker.js:825:24 at at process._tickDomainCallback (internal/process/next_tick.js:229:7)

Here's by cloud function:

exports.fcmSend = functions.database
                           .ref('/model_outputs/real_estate')
                           .onWrite((change, context) => {

    const userId  = change.after.val();
    console.log(change);
    const payload = {
          notification: {
            title: "test",
            body: "test body",
            icon: "https://placeimg.com/250/250/people"
          }
        };
  
  
     admin.database()
          .ref(`/fcmTokens/${userId}`)
          .once('value')
          .then(token => token.val() )
          .then(userFcmToken => {
            return admin.messaging().sendToDevice(userFcmToken, payload)
          })
          .then(res => {
            console.log("Sent Successfully", res);
            return null;
          })
          .catch(err => {
            console.log(err);
          });
  
  });

I've seen other people post about this error, but most of them had to do when them using 's instead of s in this case: admin.database().ref(/fcmTokens/${userId})`. As you can see, I'm using ticks, so I'm not sure what's wrong here.

Here's how the data is structured in my db: enter image description here

Obviously, I chopped off the ID, but I just wanted to illustrate that it's nested immediately under /fcmTokens.

1
You need to show more details on your database structure. Where is /model_outputs/real_estate? And what do you get with const userId = change.after.val(); console.log(userId);? - Renaud Tarnec
@RenaudTarnec /model_outputs/ is at the top level of my db. So, essentially, I want the notification to happen anytime something under /real_estate is written to. I added userID to console.log, and there was an error saying that is was undefined. - Kellen

1 Answers

2
votes

It seems that there are several problems in your Cloud Function:

#1

The following line generates an error because userId is not a String.

admin.database().ref(`/fcmTokens/${userId}`)

Most probably the value of the DB node at '/model_outputs/real_estate' is an Object and therefore when you do const userId = change.after.val(); you assign an Object to userId

Update following your comment above: It seems you get undefined for userId. You need to debug and solve this problem: it has to be a string.

#2

The following promise chaining is wrong:

admin.database()
      .ref(`/fcmTokens/${userId}`)
      .once('value')
      .then(token => token.val() )  // You don't return anything here, and not a Promise
      .then(userFcmToken => {
        return admin.messaging().sendToDevice(userFcmToken, payload)
      })
      .then(res => {
        console.log("Sent Successfully", res);
        return null;
      })
      .catch(err => {
        console.log(err);
      });

It should be something along the following lines, if I correctly understand your logic and data model:

admin.database()
      .ref(`/fcmTokens/${userId}`)
      .once('value')
      .then(snapshot => { 
        const userFcmToken = snapshot.val();
        return admin.messaging().sendToDevice(userFcmToken, payload)
      })
      .then(res => {
        console.log("Sent Successfully", res);
        return null;
      })
      .catch(err => {
        console.log(err);
      });