Push notification for web browser
I. Introduction
Have you ever seen your friend messages pop-ups on your desktop? Have you ever seen a amazon or lazada’s sale campaign information show up on your desktop even when you are not browsing those website? Those pop-ups are called push notification.
Push notification is a message pop-ups on your client’s desktops / devices. Publishers can send notifications anytime they want and receiver don’t have to be browsing website or to be in apps to receive those notifications. So why push notification are used?
For client
Push notification provide convenience and value to client. They can receive valued information at anytime like hot news, sport score, traffic, weather, flight checking, discount items, sale campaign, etc.
For publisher
Push notification is one of the most direct way to speak to user. They don’t get caught in spam filter, don’t get forgotten in an inbox. They can remind user to use app or visit website, whether they are browsing your website, open your app or not. They can also be use to drive actions, such as:
- Promotion product or offer to increase sale
- Improve customer experience
- Send transaction receipt right away
- Drive user to other marketing channels
- Convert unknown user to known customer
In this article, we’ll focus on push notification for web browser. We will figure out how it works and go through some code sample to create our very first push notification system.
Let’s begin!
II. How it works
1. Figure
2. Definition
First, we’ll go through some definition that will be used in this article
- Notification: a message displayed to the user outside of the app’s normal UI (i.e., the browser)
- Service-worker: a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction.
- Push Server: the middle server, receive request from your server and send it to clients when they are online
3. Concept
The above figure-01 explain almost everything about how Push Notification works. In this part, we are going to explain it a bit deeper.
We have 3 main part in push notification jobs: producer (your server), client (user), and push server (google GCM, google FCM, amazonSNS, etc.). Each part have their own roles:
- Client: receive push request and pop up notification.
- Server: manage client token, make push requests.
- Push server: the middle server, this will receive push request from server and send it to client when they are online.
And this is how they works
- Step 00: At first, When user browsing your website, you have to register an service-worker and ask user for permission to run service-worker. If permission is not granted, then there’s nothing we can do more. If permission granted, let’s go to next step
- Step 01: Client register itself to push server with api key and sender id to identify which app and server can send notification request to worker.
- Step 02: Push server receive register request from client and return a token to worker, this token is now present for service-worker.
- Step 03: Client receive token from push server and send it to server, server will save it to database for later use.
- Step 04: Server now had client token. From now, whenever server want to send notification to client, just send notification request to push server with client’s token to identity client and a private key to validate data.
- Step 05: When push server receive notification request, they will store it and wait for client online to send it to client. When client receive the push notification, service-worker will pop-up notification.
III. Service worker
How can we show notification even when browser wasn’t on? Because the one who show notification is the service-worker. So what is service-worker and how it works. Let’s figure it out.
1. What is service-worker
A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction. Today, they already include features like push notifications and background sync. In the future, service workers will support other things like periodic sync or geofencing. The core feature discussed in this tutorial is the ability to intercept and handle network requests, including programmatically managing a cache of responses.
The reason this is such an exciting API is that it allows you to support offline experiences, giving developers complete control over the experience.
2. Concept
A service worker has a lifecycle that is completely separate from your web page.
To install a service worker for your site, you need to register it, which you do in your page’s JavaScript. Registering a service worker will cause the browser to start the service worker install step in the background.
Typically during the install step, you’ll want to cache some static assets. If all the files are cached successfully, then the service worker becomes installed. If any of the files fail to download and cache, then the install step will fail and the service worker won’t activate (i.e. won’t be installed). If that happens, don’t worry, it’ll try again next time. But that means if it does install, you know you’ve got those static assets in the cache.
When installed, the activation step will follow and this is a great opportunity for handling any management of old caches, which we’ll cover during the service worker update section.
After the activation step, the service worker will control all pages that fall under its scope, though the page that registered the service worker for the first time won’t be controlled until it’s loaded again. Once a service worker is in control, it will be in one of two states: either the service worker will be terminated to save memory, or it will handle fetch and message events that occur when a network request or message is made from your page.
3. Sampe Code
Because of security reason – you can just register/run service-worker in some enviroment like https or localhost, etc… So for testing code, we’re gonna use localhost, you can easily build a simple server with extension “web server for Chrome”. For more information, read this
Register service-worker
main.js
if (‘serviceWorker’ in navigator) { navigator.serviceWorker.register(‘/serviceworker.js’) .then (function (reg) { console.log(‘Serviceworker registered successfully with scope: ‘, reg.scope) } .catch (function(err) { console.log(“Something wrong: “, err); }); }
serviceworker.js
self.addEventListener('install', function(event) { // Handle install event console.log('install'); }); self.addEventListener('activate', function(event) { // Handle activate event console.log('activate'); });
When you run register command, service worker will install then activate automatically.
To knwow more about service-worker register process (first time / update), read this article
Grant permission from user
Now your service worker is install and activate. But you have to grant permission from user to do stuff like notification.
main.js
notification.requestPermission(function(status) { console.log('Notification permission status:', status); });
This is one time action, after user allow or block, setting will save in notification exception
Sumary code
main.js
navigator.serviceWorker.register('/serviceworker.js').then(function (reg) { console.log(reg); }) switch (Notification.permission) { case 'granted': console.log('already granted'); break; case 'denied': console.log('blocked!!') break; case 'default': default: console.log('get permission') Notification.requestPermission(function(status) { console.log('Notification permission status:', status); }); break; };
serviceworker.js
self.addEventListener('install', function(event) { // Handle install event console.log('install'); }); self.addEventListener('activate', function(event) { // Handle activate event console.log('activate'); });
IV. SIMPLE NOTIFICATION (WITHOUT PUSH SERVER)
In this section, we will go through how to show notification directly from your website.
1. Concept
Like we discussed above, the one who pop-ups notification is service-worker. So, when you have a activated and permission granted service-worker, you now can command service-worker pop-ups notification. You can perform this command directly from your website, and yes, of course, when you perform this way, you can only pop-ups notification when user are browsing your website. Let’s go through some sample code!
2. Sample Code
Command from main.js
main.js
function displayNotification() { if (Notification.permission == 'granted') { navigator.serviceWorker.getRegistration() .then(function(reg) { reg.showNotification('Hello world!'); }) } } displayNotification();
Command from service-worker itself
E.g. Auto show notification when service-worker install
// Listen for install event, set callback self.addEventListener('install', function(event) { // Perform some task console.log('install'); self.registration.showNotification("installed"); });
V. NOTIFICATION WITH PUSH SERVER (FCM)
In this section, we will go through how full push notification system works.
1. Concept
In this tutorial, we’ll use FCM push server, FCM push server is an Google service, it’s later version of GCM. For more information, read this
2. Sample Code
a. Create FCM app
First, we register our app to FCM
Go to this website: https://console.firebase.google.com
Add project
Now let’s store our information
Public key(info)
Server(private) key
You can use server key or legacy server key to validate your push message request. I usually use the short one (legacy server key).
b. Register user to push server, get returned token
main.js
importScripts("https://www.gstatic.com/firebasejs/4.1.3/firebase.js"); // Initialize Firebase var config = { apiKey: "...", authDomain: "...firebaseapp.com", databaseURL: "https://...firebaseio.com", projectId: "...", storageBucket: "...", messagingSenderId: "..." }; firebase.initializeApp(config); // Get token const messaging = firebase.messaging(); messaging.requestPermission() .then(function() { console.log("Permission granted"); return messaging.getToken(); }) .then(function(token) { console.log(“Token: ”, token); // send token to your server here }) .catch(function(err) { console.log("Err: ", err); }) // handle receive message messaging.onMessage(function(payload) { console.log("payload: ", payload); })
Messaging is firebase’s message library. When you call messaging.requesPermission(). It will look for a serviceworker name ‘firebase-messaging-sw.js’ and then do the register stuffs. So we have to create our serviceworker to let those code run successfully.
firebase-messaging-sw.js
// welcome to firebase-messaging // do nothing
Run main.js and you will get something like this
This token now present for your browser and serviceworker. Send those token to your server and we are ready to push notification.
There are 2 case when you receive request from push server: you’re focusing on website or not. If you are NOT focusing on website, the message will go to service worker and pop-ups notification. If you are focusing, it will trigger event ‘messaging.onMessage’ so you can handle freely, of course you can command service worker pop-ups notification but in my experience it will make user feel annoyed and distracted. Let’s try our magic!
Send notification from your server to fcm server
URL: https://fcm.googleapis.com/fcm/send METHOD: POST HEADER: Content-Type: application/json Authorization: key=<your secret key> BODY: { "to": <service-worker's token>, "notification": { "title": "my title", "content": "my content" } }
When you name the data notification, service worker will pop-ups automatically if possible. Here the result,
Focusing: receive data
Un-Focusing: pop-up notification
Congratulations!! Now you have learned how to push notification. You can now improve your website, increasing your user’s experiment.
In the next section, we will go a little bit deeper about FCM, how to full-control your push event.
VI. FCM ADVANCE – EVENT HANDLE
1. Custom service worker file location and name
Like we discussed above, when you use firebase message, they will automatically find the service worker name ‘firebase-messaging-sw.js’ at home base of your website. So what will we do when we want to keep it in another location or use another file name. Here the solutions, we’re gonna register a service worker first then make firebase use this serviceworker as their firebase-messaging-sw. And here the code:
main.js
var config = { ... }; firebase.initializeApp(config); const messaging = firebase.messaging(); navigator.serviceWorker.register('/path/to/your/service-worker/serviceworker.js') .then(function(reg) { messaging.useServiceWorker(reg); messaging.requestPermission() .then(function() { console.log("Permission granted"); return messaging.getToken(); }) .then(function(token) { console.log("Token: ", token); }) .catch(function(err) { console.log("Err: ", err); }) })
2. Full-control received message
The firebase will pop-ups notification when you send notification command, but what if you want to handle data or content manually from service worker, here the solutions
- When user focusing on your website, just like before, you can handle the message in onMessage event, so there’s nothing to do more here.
- When user NOT focusing on your website, the message will be received by service worker so we’re gonna add message handle for service worker.
Let’s view the code:
serviceworker.js
messaging.setBackgroundMessageHandler(function(payload) { // do whatever you want here, in this example we pop-ups notification const title = payload.data.title; const options = { body: payload.data.content }; return self.registration.showNotification(title, options); });
** messaging.setBackgoundMessageHangler is just like messaging.onMessage event for serviceworker.
3. Handle token change
Sometimes, FCM change token of client, it’s not very critical but user will can’t receive message until next time they register the service-worker. So if you want to update token immediately, you have to handle those event. Here the code:
serviceworker.js
// Callback fired if Instance ID token is updated. messaging.onTokenRefresh(function() { messaging.getToken() .then(function(refreshedToken) { console.log('Token refreshed.'); // send new token info to server here }) .catch(function(err) { console.log('Unable to retrieve refreshed token ', err); }); });
4. Send message to group of users
This is the solutions of sending bulk push message. The idea is adding user’s tokens into groups. Whenever you want to send all message to user in that group, you just have to send only one message. Let’s figure it out.
Register token to group
Register one token
URL: https://iid.googleapis.com/iid/v1/<user’s Token>/rel/topics/<Topic name> METHOD: POST HEADER: Content-Type: application/json Authorization: key=<your secret key>
Bulk register
URL: https://iid.googleapis.com/iid/v1:batchAdd METHOD: POST HEADER: Content-Type: application/json Authorization: key=<your secret key> Cache-Control: no-cache BODY: { "to": "/topics/<TOPIC NAME>", "registration_tokens": <"Token1", “Token2”,...> }
Send push message to group
URL: https://fcm.googleapis.com/fcm/send METHOD: POST HEADER: Authorization: key=<your server secret key> Content-Type: application/json BODY { "to": "/topics/<TOPIC NAME>", "notification": { "title": "my title", "body": "my content" } }
For more information: read this
VII. NOTIFICATION – STRUCTURE AND EVENT HANDLE
Uptil now in this article, we just pop-ups the most simple notification with title and body. In this section, we will talk about all elements of an notification, and how we let them do some stuff like boozing, or open new tab onclick, etc. Let’s begin!
1. Notification structure
This is the API of showing a notification
(ServiceWorkerRegistration).showNotification(<title>, <options>);
The <title> is a string, and the <options> can be any of the following:
{ "//": "Visual Options", "body": "<String>", "icon": "<URL String>", "image": "<URL String>", "badge": "<URL String>", "vibrate": "<Array of Integers>", "sound": "<URL String>", "dir": "<String of 'auto' | 'ltr' | 'rtl'>", "//": "Behavioural Options", "tag": "<String>", "data": "<Anything>", "requireInteraction": "<boolean>", "renotify": "<Boolean>", "silent": "<Boolean>", "//": "Both Visual & Behavioural Options", "actions": "<Array of Strings>", "//": "Information Option. No visual affect.", "timestamp": "<Long>" }
2. Event handle
We will go through some important notification event
Install event: trigger after install
serviceworker.js
// Listen for install event, set callback self.addEventListener('install', function(event) { // Perform some task console.log('install'); });
Active event: trigger after active
serviceworker.js
self.addEventListener('activate', function(event) { // Perform some task console.log('activate'); });
Close event: trigger after close
serviceworker.js
self.addEventListener('notificationclose', function(event) { // Perfrom some task console.log('Closed notification’); })
Click event: trigger after click
serviceworker.js
self.addEventListener('notificationclick', function(event) { var notification = event.notification; var action = event.action; switch (action) { case <action-name>: // define in options->action break; default: // unrecognized actions (click on body) break; } })
** There are also push event, but since we use firebase messaging, we use message.onMessage or message.setBackgroundMessageHandler instead.