Firebase Cloud Messaging: Handling tokens and notifications with Node.js
Storing tokens with MongoDB and sending notifications topic and batch wise
--
In our last post, we learnt how to add notifications to an android app. We covered how to generate tokens for users, send them to a remote server and receive messages, however we didn’t engage in server side programming in that very post to keep it open for all kind of server frameworks.
We will discuss about Node.js framework in this post, and learn how to handle the tokens received from a client and then use those tokens to send notifications back to the clients. However, the setup discussed in this post is client independent and can be used for all firebase clients — Android, Web or iOS.
To get started with Node.js, we first need to install the required dependencies. We will be using express framework as our default node.js framework and BodyParser module to parse body from post requests. MongoDB will serve as our primary data store for client tokens. We will also be making use of request module for requesting external urls, get or post. The following command will install all four modules and save them in our project’s package.json.
sudo npm i express body-parser mongodb request --save
After the modules are correctly installed, we need to create an instance of express as app and add the bodyparser middleware to it. The example below does that.
We have also created an instance of MongoDB client to connect to our database (myDatabase). We will give MongoClient the location of our database using an URL. The format of the url is pretty straightforward. It begins with the protocol name, mongodb (like http or rtsp), followed by the location address and port number of the mongodb database. The address should be localhost or 127.0.0.1 if the database (mongod daemon) is running on the same server (machine). If running in a different location, the ip address of that server is required as address. The default port number which mongodb runs on is 27017. If using a different port number, we need to specify that. The last part of the URL is the name of the database, which in our case is myDatabase.
We will declare an endpoint where we will send our tokens. Let’s add a POST endpoint, store, which will get our token out of the request body and using MongoClient, store it in tokens collection of myDatabase. We try to connect to the database with the url we declared earlier. Once connected, the connect method gives database (db) in need as well as an exception object as callback. If no exception occurs, we will insert the token into the tokens collection using collection’s insertOne method. In the example above, the post body parsed from request, req.body, is passed directly as the data to be inserted as it contains token only. If there are more key-value pairs in it, we can get token value out of the body and then put it as insertion data.
let tokenValue = req.body.token
let data = { token: tokenValue }
If data is inserted without any error, we will again get a callback where we send response ok with status code 200. After that, just close the database.
Okay, so we are done with storing tokens. Now, we use these tokens to send notifications. Let’s declare a new function, sendNotification that accepts message data.
The data sendNotifications function accepts is pure JSON. We need to change it into its string equivalent using JSON.stringify(…) to be able to send it to FCM endpoint.
const headers = {
'Authorization': 'key=<your firebase legacy server key>',
'Content-Type': 'application/json',
'Content-Length': dataString.length
}
FCM endpoint, other than data, also requires our legacy server key as one of the request headers. We can get the lagacy server key from Firebase Console > Settings (the icon on top-right) > Cloud Messaging. Notice the headers object declared above has an Authorization key. This very key holds the server key we just collected from Firebase Console. However, there’s a catch. Most people working with authorization key for the first time often ignore including key= with the server key. Don’t! Also, we add Content-Type and Content-Lengthheaders in the headers object just to be sure that FCM server can parse the header and thus, the key. Both headers are self-explanatory.
Once we have the header ready, we send a POST request to FCM server with the header and message data using request and for that we need a properly structured options object containing FCM endpoint (uri), headers, method of request, POST, and ofcourse, json (data). If having any issue understanding, go to fcm3.js and look at the code, it’s very straightforward.
Okay, we have our base notification sending function ready. We now need to identify the notification type and prepare data message accordingly. We first start with topic based notification. Topic based notifications are used in combination with GCMPubSub, a tool that lets users subscribe to certain topics and then receive notifications only for those topics. We’ll talk about GCMPubSub some other day. The example (fcm4.js) above details about the data object we need to create. You’ll notice that the data object contains the obvious bodyand title. Feel free to add more data in it.
The only difference between topic based notification and normal notification is the target audience. Topic based notification expects a to key with the topic (s) of interest. Once done adding topics to data object, just call the sendNotifications(..) and then send a response (status code 200, ok) back to the NotificationCenter (we will talk about it in a moment).
While topic based notifications expect topics, normal notifications require tokens. The data object remains the same as topic based, but instead of to, we need registration_ids to hold the token array.
The sendToAll(…) function we use to send token based notifications also accepts regIdArray, other than usual message, title and response. The regIdArray is an array of all token objects we have stored in the database. Look below (fcm6.js) for a hint. Since it’s an array of objects and not string, we need to map it from object to plain string array. We will use Array’s map method for that. You will notice in the example above that we tried to batch the registration ids (tokens) in small arrays of 1000. The reason behind it is we are not allowed to send a notification to more than 1000 ids at once. We use Array’s slice method to create smaller arrays of length 1000.
data[‘registration_ids’] = regIdArray.slice(start, end).map((item) => {
return item[‘token’]
})
The following piece of code does that, assuming the key for token value is token. A for loop for every 1000 registration ids runs batching them and calling sendNotification(…) function everytime. Finally an okresponse is sent back.
Now, it’s time to identify the notification type. We will do that in the notify endpoint we declare for the Notification Center (where notification message is prepared). Look at the html page declared below. We have two radio buttons. One for sendToAll, the other for Topic based. We also have a select (drop down) menu to choose the topic in case Topic based radio button is selected. The form will pick the appropriate data as per the selection and send it to our notify endpoint (POST). We will get our message, title, notification type and if availabe, topic.
If the type is topic, we call sendToTopics(…) passing message, title, topic and response object. If not, we connect to our database using MongoClient and call sendToAll(…). The parameters passed to it will remain same except one. Topic is replaced with tokens array we receive from the database call (find?).
That’s it. We have successfully created two end points, one for storing tokens and the other for sending notification using a topic or tokens. We have also created a Notification center (notifcenter.html) using html, css and a little JavaScript to let us create notification body and select its type. Notification center is available as a get request at ‘/notifications’ (see fcm1.js). Feel free to share your issues or experience in comments below.
Note: This article was first published on Context Neutral. Do visit to find more relevant and updated articles on Android, Node.js, AWS and more.