Firebase Cloud Messaging for Web using JavaScript

Posted on Feb 21, 2018 #firebase

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets us reliably deliver messages at no cost. Using FCM, we can send notification messages that are displayed to the user, or send data messages and determine completely what happens in the application code. It distributes messages to the client app in any of 3 ways- to single devices, to groups of devices, or to devices subscribed to topics.

At first, I found it quite challenging to work with FCM as the documentation is not clear. So, I wrote a basic guide to using FCM for Web using JavaScript.

Download source code from Github.

Add Firebase to our js file

First, we’ll need to include Firebase to a JavaScript app. In our case, it’ll be a simple javascript file. To add Firebase to our js file, we’ll need a Firebase project, the Firebase SDK, and a short snippet of initialization code that has a few details about the project. Follow Add Firebase to your app to get the config snippet.

The final structure of our project will be like this:

fcm-demo
├── firebase-messaging-sw.js
├── index.html
├── manifest.json
└── script.js

Whitelist the GCM Sender ID

Add a web app manifest that specifies the gcm_sender_id, a hard-coded value indicating that FCM is authorized to send messages to our js file. Do not change the value of gcm_sender_id. Create a manifest.json file and write:

{
    "gcm_sender_id": "103953800507"
}

Add Firebase Messaging Service Worker

We have to create a Service Worker file that will receive and display web notifications. Create a file named firebase-messaging-sw.js at the root of the project with the following content:

// Give the service worker access to Firebase Messaging.
importScripts('https://www.gstatic.com/firebasejs/4.9.1/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/4.9.1/firebase-messaging.js')

// Initialize the Firebase app in the service worker by passing in the messagingSenderId.
var config = {
    messagingSenderId: "your_messaging_sender_id"
};
firebase.initializeApp(config);

// Retrieve an instance of Firebase Data Messaging so that it can handle background messages.
const messaging = firebase.messaging()
messaging.setBackgroundMessageHandler(function(payload) {
  const notificationTitle = 'Data Message Title';
  const notificationOptions = {
    body: 'Data Message body',
    icon: 'alarm.png'
  };
  
  return self.registration.showNotification(notificationTitle,
      notificationOptions);
});

Request Permissions and Get FCM Device Tokens

Create another file called script.js and add:

// Initialize the Firebase app by passing in the messagingSenderId
var config = {
  messagingSenderId: "your_messaging_sender_id"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();

navigator.serviceWorker.register('firebase-messaging-sw.js')
.then(function (registration) {
    messaging.useServiceWorker(registration);
        
    // Request for permission
    messaging.requestPermission()
    .then(function() {
      console.log('Notification permission granted.');
      // TODO(developer): Retrieve an Instance ID token for use with FCM.
      messaging.getToken()
      .then(function(currentToken) {
        if (currentToken) {
          console.log('Token: ' + currentToken)
          sendTokenToServer(currentToken);
        } else {
          console.log('No Instance ID token available. Request permission to generate one.');
          setTokenSentToServer(false);
        }
      })
      .catch(function(err) {
        console.log('An error occurred while retrieving token. ', err);
        setTokenSentToServer(false);
      });
    })
    .catch(function(err) {
      console.log('Unable to get permission to notify.', err);
    });
});

// Handle incoming messages
messaging.onMessage(function(payload) {
  console.log("Notification received: ", payload);
  toastr["info"](payload.notification.body, payload.notification.title);
});

// Callback fired if Instance ID token is updated.
messaging.onTokenRefresh(function() {
  messaging.getToken()
  .then(function(refreshedToken) {
    console.log('Token refreshed.');
    // Indicate that the new Instance ID token has not yet been sent 
    // to the app server.
    setTokenSentToServer(false);
    // Send Instance ID token to app server.
    sendTokenToServer(refreshedToken);
  })
  .catch(function(err) {
    console.log('Unable to retrieve refreshed token ', err);
  });
});

// Send the Instance ID token your application server, so that it can:
// - send messages back to this app
// - subscribe/unsubscribe the token from topics
function sendTokenToServer(currentToken) {
  if (!isTokenSentToServer()) {
    console.log('Sending token to server...');
    // TODO(developer): Send the current token to your server.
    setTokenSentToServer(true);
  } else {
    console.log('Token already sent to server so won\'t send it again ' +
        'unless it changes');
  }
}

function isTokenSentToServer() {
  return window.localStorage.getItem('sentToServer') == 1;
}

function setTokenSentToServer(sent) {
  window.localStorage.setItem('sentToServer', sent ? 1 : 0);
}

Finally Combine All Files

In index.html file:

<!-- <!DOCTYPE html> -->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="manifest" href="manifest.json">
    <link rel="shortcut icon" href="favicon.ico">
    <title>FCM Demo</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
</head>
<body>
    <h1 style="text-align:center;">
        Firebase Cloud Messaging Demo
    </h1>
    <script src="http://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
    <script src="https://www.gstatic.com/firebasejs/4.9.1/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/4.9.1/firebase-messaging.js"></script>
    <script src="script.js"></script>
</body>
</html>

Run Application

Firebase will not allow running the index.html file directly. So, I will be running the index.html file using Python server. Open terminal, navigate to the project directory and run:

python -m SimpleHTTPServer 8000

Open http://localhost:8000 in the browser. A popup will appear to ask for permission to show notifications in the browser. Click Allow and if everything is fine up to this point, a client token will be generated.

Send Messages

To send a notification, we also need Firebase app’s Server key along with device token. To get it open Firebase app’s Firebase Console > Project Settings > Cloud Messaging and copy the Server Key.

To send a notification, send the following HTTP request using cURL in the command line:

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "New Article",
    "body": "Firebase Cloud Messaging for Web using JavaScript",
    "icon": "alarm.png",
    "click_action": "http://rakibul.net/fcm-web-js"
  },
  "to": "<your_client_token>"
}' "https://fcm.googleapis.com/fcm/send"

Send Messages to Topics

FCM topic messaging allows sending a message to multiple devices that have opted into a particular topic. We can compose topic messages as needed, and FCM handles routing and delivering the message reliably to the right devices.

Subscribe the Client App to a Topic

Given a registration token and a topic name, we can add the token to the topic using the Google Instance ID server API:

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -H "Content-Length: 0" "https://iid.googleapis.com/iid/v1/<your_instance_token>/rel/topics/<topic_name>"

Subscribe Multiple Client Apps to a Topic

Using the Instance ID service’s batch methods, we can perform batch subscription of app instances:

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -d '{
  "to": "/topics/<topic_name>",
  "registration_tokens": ["<your_client_token1>", "<your_client_token2>", ...]
}' "https://iid.googleapis.com/iid/v1:batchAdd"

Unsubscribe Multiple Client Apps from a Topic

We can also perform bulk removal of app instances to an FCM topic. Unfortunately, Google Instance ID server API does not provide functionality for simple app unsubscription from a topic. But we can pass a single client token to this API to unsubscribe the single client.

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -d '{
  "to": "/topics/<topic_name>",
  "registration_tokens": ["<your_client_token1>", "<your_client_token2>", ...]
}' "https://iid.googleapis.com/iid/v1:batchRemove"

Message Types

FCM can send two types of messages to clients:

  • Notification messages: Sometimes thought of as “display messages.” These are handled by the FCM SDK automatically.
  • Data messages: Handled by the client app. Maximum payload for both message types is 4KB.

Send Notification Messages to a Topic

Notification messages contain a predefined set of user-visible keys. To send messages to a specific topic:

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "New Article",
    "body": "Firebase Cloud Messaging for Web using JavaScript",
    "icon": "alarm.png",
    "click_action": "http://rakibul.net/fcm-web-js"
  },
  "to": "/topics/<topic_name>"
}' "https://fcm.googleapis.com/fcm/send"

Send Data Messages to a Topic

Data messages contain only your user-defined custom key-value pairs. To send messages to a specific topic:

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -d '{
  "data": {
    "title": "New Article",
    "body": "Firebase Cloud Messaging for Web using JavaScript",
    "icon": "alarm.png",
    "click_action": "http://rakibul.net/fcm-web-js"
  },
  "to": "/topics/<topic_name>"
}' "https://fcm.googleapis.com/fcm/send"

Also Notification messages can contain an optional data payload:

curl -X POST -H "Authorization: key=<your_server_key>" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "New Article",
    "body": "Firebase Cloud Messaging for Web using JavaScript",
    "icon": "alarm.png",
    "click_action": "http://rakibul.net/fcm-web-js"
  },
  "data": {
    "title": "rakibul.net",
    "body": "This is a background notification"
  },
  "to": "/topics/<topic_name>"
}' "https://fcm.googleapis.com/fcm/send"

Get Information about Client App

To get information about an app instance, call the Instance ID service at this endpoint, providing the app instance’s token as shown:

curl -X GET -H "Authorization: key=<your_server_key>" "https://iid.googleapis.com/iid/v1/<your_client_token>?details=true"

Visit About FCM Messages to learn more about FCM messages.
Visit Topic Messaging on Web/JavaScript to learn more about topic messaging.
Visit Server Reference to learn more about server reference API.

comments powered by Disqus