Installing the Firebase Admin SDK
Since the Node.js code written in the remainder of this chapter is going to make extensive use of the Firebase Admin SDK the next step is to install this module using the npm command. Remaining in the directory containing the package.json file, install the module library as follows:
npm install firebase-admin --save
A sub-directory named node_modules containing the firebase-admin module will have been created and a review of the package.json file will show that the firebase-admin module is now listed as a dependency for the current project:
. . "dependencies": { "firebase-admin": "^5.0.0" . .
Generating the Service Account Credentials
Before any Firebase cloud messages can be sent using Node.js, an additional JSON file needs to be generated and installed on the server. This file contains a private key that allows Node.js code to communicate through the SDK to the Firebase messaging service and is generated from within the Firebase console.
Open the Firebase console in a browser window, select the Firebase Examples project and select the Settings option as highlighted in Figure 27-1:
Figure 27-1
On the settings page, select the Service Accounts tab followed by the Firebase Admin SDK option:
Figure 27-2
Click on the Generate New Private Key button, download the generated file and place it in a suitable directory on the system on which Node.js has been installed. This can be in the directory containing the package.json file or any other directory where it can be referenced within Node.js code. Regardless of where the file is placed, it is important to keep this file secure to prevent others from using your account to send Firebase cloud messages. If the key security is compromised, return to the Firebase console and generate a new one.
The Firebase Admin SDK page also includes a useful Node.js snippet that will be used in the next section to perform some initialization tasks so keep this page open in the browser for now.
Initializing the Admin SDK
With Node.js installed and configured, work can begin writing code to send a message to a device. Once again using the editor of your choice, create a new file named send.js and copy and paste the Node.js snippet from the Firebase console web page into the file:
var admin = require("firebase-admin"); var serviceAccount = require("path/to/serviceAccountKey.json"); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), databaseURL: "<your database URL here>" });
The code imports the firebase-admin module and then declares a variable referencing the location of the JSON file containing the private key generated earlier in the chapter. Modify the placeholder path to reference the actual name and location of the file, for example:
var serviceAccount = require("/home/neil/nodejs/firebase-adminsdk.json");
Wrapping the path in a require statement ensures that an error will be thrown if the file does not exist at the specified location.
Note also that the databaseURL for the Firebase project is included in the arguments passed to the initializeApp method. This is the URL for the realtime database associated with the project. If the initialization snippet was copied from the Firebase console this should already be set to the correct URL.
Adding the Destination Registration Token
For the purposes of this example, a message will be sent only to a specific device. Locate the registration token for the device and app combination tested in the previous chapter and assign it to a variable in the send.js file beneath the SDK initialization code:
. . var registrationToken = "<registration token goes here>";
Understanding Message Payloads
The content of a message is referred to as the payload. Firebase messaging supports notification, data and combined messages.
• Notification Messages - Consist of a title and a message body and trigger the notification system on arrival at the device. In other words, an icon will appear in the status bar and an entry will appear in the notification shade. Such notifications should be used when sending an informational message that you want the user to see.
• Data Messages - Contain data in the form of key/value pairs and are delivered directly to the app without triggering the notification system. Data messages are used when sending data silently to the app.
• Combined Messages – Contain a payload comprising both notification and data. The notification is shown to the user and the data is delivered to the app.
The following example declares a notification payload consisting of a title and a message body:
var payload = { notification: { title: "Account Deposit", body: "A deposit to your savings account has just cleared." } };
A data message, on the other hand, uses the data keyword together with one or more key/value pairs:
var payload = { data: { account: "Savings", balance: "$3020.25" } };
A combined message payload contains both notification and data elements as follows:
var payload = { notification: { title: "Account Deposit", body: "A deposit to your savings account has just cleared." }, data: { account: "Savings", balance: "$3020.25" } };
The payload may also have options associated with it. The main option keys are as follows:
• collapseKey – Used to identify a group of messages where only the most recent message is sent if multiple messages are waiting to be delivered to the device.
• contentAvailable – Used when sending messages to iOS devices. When set to true, the app is woken on message receipt. This is the default behavior for messages received on Android devices.
• dryRun – When set to true the message is not actually sent. Useful for testing purposes during development.
• mutableContent – Applies only to iOS client apps and when set to true allows the app to modify the message content before it is presented to the user as a notification.
• priority – A string value that may be set to “high” or “normal”. By default notifications are high priority while data messages default to normal priority. Normal priority messages may be delayed but impose a lower burden on the device battery. High priority messages, on the other hand, are sent immediately, wake sleeping devices and open a network connection to the corresponding server.
• timeToLive – A numeric value indicating the amount of time in seconds that the message should be held if the destination device is offline. This can be set to any duration up to four weeks (2419200 seconds). If no TTL is specified the default is four weeks.
The following is an example option declaration that sets the priority to normal with a time to live duration of one hour:
var options = { priority: "normal", timeToLive: 60 * 60 };
Defining the Payload and Sending the Message
Edit the send.js file and add the following payload and options declaration beneath the registration token variable:
var payload = { notification: { title: "This is a Notification", body: "This is the body of the notification message." } }; var options = { priority: "high", timeToLive: 60 * 60 *24 };
All that remains is to send the message. This involves a call to the sendToDevice() method of the Firebase Admin SDK, passing through the registration token, payload and options and then checking the response to find out if the message was sent successfully:
admin.messaging().sendToDevice(registrationToken, payload, options) .then(function(response) { console.log("Successfully sent message:", response); }) .catch(function(error) { console.log("Error sending message:", error); });
Testing the Code
Run the Messaging app created in the previous chapter on the device or emulator session that matches the reference token included in the message and place it in the background. Within the terminal or command prompt window, run the following command:
node send.js
After a short delay, output similar to the following should appear indicating that the message was send successfully:
Successfully sent message: { results: [ { messageId: '0:1493066541057227%00ae8e2b00ae8e2b' } ], canonicalRegistrationTokenCount: 0, failureCount: 0, successCount: 1, multicastId: 7678894013550453000 }
Refer to the device or emulator and note the appearance of the notification icon in the status bar. Drag down from the status bar to reveal the notification shade and tap on the notification sent from the Node.js server. The notification body text should now appear in the TextView in the main activity.
Next, change the payload in the send.js file to a data message:
var payload = { data: { MyKey1: "Hello" } };
Send the message again with the app in the foreground and note that the data is output to the Android Studio logcat panel. Send the message once more, this time with the app in the background. Note that no notification is triggered but that the value is once again displayed in the logcat output. Clearly the message was still received even though no notification was triggered on the device.
Topics
Firebase FCM topics offer developers a publish and subscribe system that allows apps to opt-in to receiving messages within certain categories. A news app might, for example, provide the user with the option of subscribing to notifications for different categories of news headlines such as business, local and international news.
A client app can be subscribed to a topic either from within the app itself, or on the server using the app’s registration token.
A client app subscribes itself to a topic by making a call to the subscribeToTopic() method of the FirebaseMessaging instance. If the topic does not already exist, the method call creates the topic. The following code, for example, subscribes an app to a topic named “finance”:
FirebaseMessaging.getInstance().subscribeToTopic("finance");
A call to the unsubscribeFromTopic() method of the FirebaseMessaging instance (passing through the topic string as an argument) will opt the app instance out of receiving further messages for that topic.
FirebaseMessaging.getInstance().unsubscribeFromTopic("finance");
To subscribe an app instance from the server using Node.js, a call must be made to the Admin SDK subscribeToTopic() method passing through the registration token of the app/device combination and the name of the topic:
var registrationToken = "<registration token here>"; var topic = "finance"; admin.messaging().subscribeToTopic(registrationToken, topic) .then(function(response) { console.log("Successfully subscribed to topic:", response); }) .catch(function(error) { console.log("Error subscribing to topic:", error); });
Multiple app instances may be subscribed to a topic by passing through an array of registration tokens as follows:
var registrationTokens = [ "<registration token one>", "<registration token two>", … ]; var topic = "finance"; admin.messaging().subscribeToTopic(registrationTokens, topic) .then(function(response) { console.log("Successfully subscribed to topic:", response); }) .catch(function(error) { console.log("Error subscribing to topic:", error); });
To opt client apps out from a topic using Node.js on the server, simply call the unsubscribeFromTopic() method passing through a registration token (or array of tokens) together with the topic name string. Messages may be sent to subscribed devices either from a server, or using the Notifications panel of the Firebase console. To send a notification to topic subscribers within the Firebase console, compose a new notification message and select the Topic option within the Target section of the message composition screen. With the Topic option selected, type the first few letters of the topic name into the text field to see a list of matching topics to which the message is to be sent:
Figure 27-3
Note when using the Firebase console to send notifications to topic subscribers that it can take up to 24 hours for a newly created topic to appear within the topic list.
To send a message to subscribed client apps from a server using Node.js, the sendToTopic() Admin SDK method is called passing through the payload and topic string. In the following code fragment, a notification message is sent to all “finance” topic subscribers:
var payload = { notification: { title: "NASDAQ News", body: "The NASDAQ climbs for the second day. Closes up 0.60%." } }; var topic = "finance"; admin.messaging().sendToTopic(topic, payload) .then(function(response) { console.log("Successfully sent message:", response); }) .catch(function(error) { console.log("Error sending message:", error); });
When sending messages to a topic, Firebase supports the use of regular expressions ([a-zA-Z0-9-_.~%]+) to target multiple topics based on matching criteria. Assuming three topics named news_local, news_business and news_politics, for example, the following expression would send a message to all three topics:
var topic = "news_*"; admin.messaging().sendToTopic(topic, payload) . .
Using Topic Conditions
Topic conditions may also be used when sending messages to topics. Conditions allow the sender to define the terms under which a subscriber is eligible to receive a message. This is defined based on the topics to which the app is subscribed.
Topic conditional expressions support both the AND (&&) and OR (||) operators and are used with the sendToCondition() Admin SDK method. In the following code, for example, the message will be received by an app only if it has subscribed to the news topic while also being subscribed to either the finance or politics topics:
var condition = "'news' in topics && ('finance' in topics || 'politics' in topics')"; admin.messaging().sendToCondition(condition, payload) .then(function(response) { console.log("Successfully sent message:", response); }) .catch(function(error) { console.log("Error sending message:", error); });
When working with topic conditions, the conditional expressions are limited to two conditional statements.
Summary
This chapter has outlined and demonstrated the use of Node.js and the Firebase Admin Node.js SDK to send and manage Firebase cloud messages from a server environment. This involves the installation of the Node.js environment and Admin SDK configured with the appropriate Firebase account credentials. When an app is installed on a device it is assigned a registration token which uniquely identifies that combination of device and app. Using this token, messages can be targeted to specific devices from the server. Messages may also be targeted to devices where the app has subscribed to specific topics. As will be outlined in the next chapter, messages may also be sent to multiple devices through the use of device groups.
Comments
Post a Comment