I have heard about Web push notifications and Service Workers many times, but I have never implemented them until the last week for Just Comments. So I decided to share my notes about this technology.
First of all, there are plenty of articles and useful documentation about Web push notifications so I won’t add something new to it. I will try to summarize what I feel one needs to understand and know to get started quickly and implement a basic version of Web Push notifications.
Secondly, there are many platforms out there on the market that provide web push notifications service. Some of them have free trials, SendPulse, for instance, goes an extra mile and offers its users the Web Push feature for free.
In this tutorial, I won’t use any service like this and I will show how to implement similar functionality from scratch.
Let’s start with some key facts:
#1 Push notifications rely on multiple separate browser APIs
The entire feature of Push Notifications involves Push API and Notifications API which can be used separately. Also, it relies on Service Workers because only they can run in the background to be able to receive push notifications even if the user closed the sending website.
#2 Not all browsers support Push Notifications (yet)
Currently, IE && Opera Mini doesn’t support Service Workers and Push API and, additionally, iOS Safari, Chrome for Android, Samsung Browser and UC browser for Android don’t support Web Notifications API.
It means that you need to detect if the browser supports required APIs or not when you implement push notifications.
Big Diagram of How This Works
I created a simple diagram to wrap my head around different APIs and concepts. It connects various pieces via a sequence of actions/relationships between them.
So all starts with some JavaScript code on your Web page which registers (1) a service worker. The service worker provides (2) a service worker registration object which, in turn, gives access (3) to an instance of Push Manager. The push manager can provide (4) us a Push Subscription if the user allows. This subscription object can be sent to your backend (5). On the server, you can use the subscription to send a message (6) to the user’s browser. The service worker receives (7) the message, but it is not automatically shown to the user. The service worker parses the message and can decide what to do with it. Normally, the worker displays a Notification (8) using the Notification API.
The Coding Part
As I mentioned in the key facts, not all browsers support the APIs, so we need to check if the user’s browsers support service workers:
function supportsPushNotifications() {
return 'serviceWorker' in navigator && 'PushManager' in window;
}
supportsPushNotifications() === true // if the browser supports needed APIs
After we check that we can use Service Workers, we can start with the first step and register a service worker:
navigator
.serviceWorker
.register('/sw.js')
.then(swRegistration => {
// TODO step 2 & 3 here
})
.catch(err => {
console.log('serviceWorker.register failed', err);
});
This code fetches sw.js
at the root of your website. The register
function returns a promise. Therefore, we handle it with .then
for a successful case and .catch
if something goes wrong.
Now, we can implement the step 2 & 3 which requires the swRegistration
:
const applicationServerKey = '';
swRegistration
.pushManager
.getSubscription()
.then(subscription => {
const isSubscribed = !(subscription === null);
if (!isSubscribed) {
return swRegistration.pushManager
.subscribe({
userVisibleOnly: true,
applicationServerKey,
})
.then(sendSubscriptionToServer);
}
sendSubscriptionToServer(subscription);
})
.catch(err => {
console.log('getSubscription failed', err);
});
Don’t worry about applicationServerKey
for now. applicationServerKey
allows associating the subscription with your server. I will explain how to obtain this key later.
So what happens here: we call the pushManager.getSubscription
method which returns a subscription if the user has already allowed push notifications and null
otherwise. If we already have a subscription, we can send it to the backend. If not, we call pushManager.subscribe
to ask the user to allow push notifications.
Now, for the step 5, you can use any method you like to send the subscription object to your server. I recommend stringifying it first with JSON.stringify(subscription)
.
For sending a message from your server to the client, I recommend using the web-push
module:
const webpush = require('web-push');
const vapidKeys = {
publicKey: '',
privateKey: '',
};
webpush.setVapidDetails(
'mailto:your@email',
vapidKeys.publicKey,
vapidKeys.privateKey
);
webpush.sendNotification(
JSON.parse(subscription),
JSON.stringify({
title: 'Title',
icon: 'https://your-site.com/assets/push-icon.png',
body: 'Body',
url: 'https://your-site.com/url-to-open',
})
)
And now in steps 7 & 8, we circle back to sw.js
, the service worker code responsible for receiving and displaying a push message:
self.addEventListener('push', function(event) {
const message = JSON.parse(event.data.text());
const title = message.title;
const url = message.url;
const options = {
body: message.body,
icon: message.icon,
badge: message.badge,
data: url,
};
event.waitUntil(self.registration.showNotification(title, options));
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data));
});
Here, two event listeners are defined. With the push
listener, we parse the message and invoke the showNotification
method to display a notification. On notificationclick
we close the notification and navigate to the URL sent via the push message.
Generating Keys
You can use the web-push
library to generate keys. The key generation needs to be done once. Both the frontend and the backend use the same public key, and the backend only uses the private key:
const webpush = require('web-push');
const vapidKeys = webpush.generateVAPIDKeys();
console.log(vapidKeys);
You should specify the keys in the previous code snippets, where needed.
Conclusion
I find the APIs for Push Notifications to be quite simple and straightforward. Nevertheless, it takes quite some time, in the beginning, to wrap your head around all concepts and moving parts.
I hope you find these notes useful and you’ll come back to them once you need to implement push notifications. And if this time comes, please don’t ask for permissions as soon as the web page loads: it’s annoying, and most people would block it.
Thanks for reading!