Web Push Notification — how to

Lukas Liesis
3 min readDec 7, 2020

Main building blocks:

  1. Client side script
  2. Client side web worker
  3. Backend script
  4. Browser vendor service which provides URL and credentials where you push notification from your backend

Couple super important notes:

Note: It requires HTTPS. No https — no push notifications.

Note: Each device and browser has different push notification URL which you need to get to send notification to that browser on that device. If subscribed on notifications on phone on chrome, it will work with that chrome on that phone.

Note: Each browser has 3rd party push notification service which provides glue between backend and client. Backend pushes data to this service and service pushes data to browser even if browser is closed. You don’t care about who is that service, just use pushManager.subscribe to get URL and credentials of it and use it on backend to push notification.

Note: If you use ReactJS react-create-app it by default adds the line below. Remove it or you will get random errors. Some will say service worker is not active/available. Some will say issues about storage or else. Make sure your app is not unregistering the service worker at some place.

// this will destroy your notifications code
serviceWorker.unregister();

Client side script is divided in several steps:

  1. Request permission to send notifications
const permissionResult = await Notification.requestPermission();
console.log('permission result: ',permissionResult);

2. Register service worker file (pasted worker sample on bottom of this article)

const sw = await navigator.serviceWorker.register('path/to/service-worker-web-push-notifications.js');
console.log('register result: ',sw);

3. Use service worker’s pushManager to get subscription URL and credentials (to get applicationServerKey — check server side 1st step)

let push = await sw.pushManager.subscribe({
userVisibleOnly: true, // it is required, always true.
applicationServerKey: 'PUBLIC_KEY_FROM_BACKEND_1_STEP',
});
console.log(`push: `, JSON.stringify(push));

Push result must be something like this:

{
"endpoint": "https://.....SERVICE..URL..OF..BROWSER..MAKER....",
"expirationTime": null,
"keys": {
"p256dh": "..............",
"auth": ".........."
}
}

This url and credentials are the thing you need to push notification to the user.

Serve side has couple steps:

  1. To get subscription URL and credentials in step 3 of client side — you need applicationServerKey — get this from backend:
const webPush = require('web-push');
console.log(webPush.generateVAPIDKeys());

Once you have VAPID keys, save those. It look like so:

{
publicKey: '.....LONG....LONG...STRING...',
privateKey: '...SHORTER....STRING....'
}

publicKey is used in client side on step 3 as applicationServerKey
both keys are saved on backend for future

2. Use saved public and private keys from step 1 to set VAPID detaild

const webPush = require('web-push');

const vapidKeys = {
publicKey: '.....LONG....VAPID..PUBLIC..KEY.....',
privateKey: '...SHORTER..VAPID..PRIVATE..KEY....',
};

webPush.setVapidDetails(
'mailto:your-mail@something.com',
vapidKeys.publicKey,
vapidKeys.privateKey,
);

For the first argument of setVapidDetails I haven’t found any real sample of when and how it may be used, that’s the closest i found:

This string needs to be either a URL or a mailto email address. This piece of information will actually be sent to the web push service as part of the request to trigger a push. The reason this is done is so that if a web push service needs to get in touch with the sender, they have some information that will enable them to.
— web

3. Use user’s push URL and credentials you got at client side step 3 to send the notification

const userDetail = {
'endpoint': 'https://.....SERVICE..URL..OF..BROWSER..MAKER....',
'expirationTime': null,
'keys': {
'p256dh': '.............',
'auth': '...........',
},
};

4. Send the message

const sub = {
endpoint: userDetail.endpoint,
expirationTime: null,
keys: {
p256dh: userDetail.keys.p256dh,
auth: userDetail.keys.auth,
},
};
webPush.sendNotification(sub, 'test message from backend 2134');

Now on client side you can get the message and do something with in inside the file you registered for service worker in client side step 2

service-worker-web-push-notifications.js:

self.addEventListener('push', function (event) {
console.log('#24gdgg Received a push message', event);

// var title = 'Yay a message.';
// var body = 'We have received a push message.';
// var icon = '/images/icon-192x192.png';
// var tag = 'simple-push-demo-notification-tag';

event.waitUntil(
self.registration.showNotification('hi title', {
body: 'hi body',
// icon: icon,
// tag: tag,`
}),
);
});

self.addEventListener('notificationclick', function (event) {
console.log('#4grfghest On notification click: ', event.notification.tag);
// Android doesn’t close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();

// This looks to see if the current is already open and
// focuses if it is
// event.waitUntil(clients.matchAll({
// type: 'window',
// }).then(function (clientList) {
// for(var i = 0; i < clientList.length; i++) {
// var client = clientList[i];
// if (client.url === '/' && 'focus' in client) {
// return client.focus();
// }
// }
// if (clients.openWindow) {
// return clients.openWindow('/');
// }
// }));
});

--

--

Lukas Liesis

2 decades exp of building business value through web tech. Has master's degree in Physics. Built startups with ex-Googlers. Was world’s top 3% developer.