Get started with Web Push Notification. How to implement Push Notification in JavaScript

Push Notifications in JavaScript? Yes, you can!

With working code samples and easy explanations.

Lorenzo Spyna
ITNEXT
Published in
10 min readAug 27, 2019

--

The final result

TLDR: You suck, you just want the code.

You can see a live demo of what we are going to create here:

You can download and run locally the demo, the source code with full instructions is here:

You are going to unveil the secrets of push notifications, ready? Let’s start.

First fact: push notifications are made of two things:

  • Push: the act of sending a notification from a server to an application. Yes, you will need a server 😅.
  • Notifications: the actual notification displayed in the status bar of your smartphone or on the browser.

The Web push notifications flow

The Web push notification is a protocol that implies 4 actors:

  • the User: that wants to receive the notifications.
  • the Application: that runs on the user-agent (typically the browser)
  • the Service worker: that runs on the browser
  • the Push Server: sends push messages to the service worker
This is the flow to implement Web push notification
The push notification implementation flow
  1. The flow starts when the application asks the user to consent to display the notifications.
The browser asks the user consent to display notifications
The browser asks for the user's consent to display push notifications

2. Once the user accepted to receive the notification, the application registers a service worker (I’ll explain later what a service worker is).

4. When the app receives the service worker registration, it can use it to create a push notification subscription. The registration creates, with some other thing, an endpoint to send push messages to.

7. At this point, the application sends the push notification registration to the push server. The push server needs the endpoint of the subscription to send messages to.

8. When the push server receives the push subscription it saves it.

9. The push server sends a push message to the endpoint of the subscription, next, the message is received by the service worker, which has a listener to push events.

10. The service worker displays the notification to the user

11. When the user clicks the notification or the notification actions (you’ll see later what actions are) the service worker receives the click because it has a listener to the notification click event.

12. When the service worker receives the click, it can do pretty much whatever it wants with the notification and the notification data, like displaying a webpage, calling an API, or whatever your imagination suggests you.

Now that you know the flow is time to move on with the real implementation.

Notifications

Ask user permission

To send notifications, you need to ask the user for permission. Notifications cannot be sent without user consent. The user can:

  • Do nothing: default the notification won’t be displayed
  • Grant: granted the notification will be displayed
  • Deny: denied the notification won’t be displayed

What is a Notification

A notification is a JavaScript object with some properties and methods: title, data, icon, image, and so on. we’ll have a look at these properties later.

A good starting point is typing new Notification(). Bang 🤟! you’re done.

Let’s add a title and some options to make it look better. At this link, you can see the full list of options.

Sample code to show a web Notification

The above code snippet shows up a Push Notification with a title, text, an image, an action, and some other good stuff. The result will look like this on your smartphone:

The resulting notification will show like this image
Here’s the resulting notification

As you can see in the code sample ( navigator.serviceWorker.ready...) to display this kind of notification you need to use a service worker.

Service workers

According to Mdn:

Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available).

To make it easy we can say a service worker is a JavaScript file that runs in the background of the application in a separate thread. It can “talk” with the application, it must be registered, has no access to the dom, and runs only under HTTPS or in localhost for development.

Service workers are easy once you understand how they work.

Putting things together

To make push notifications work, we are going to create:

  • A JavaScript file that asks for the user's permission
  • A service worker that manages the notifications

Let’s create a file called push-notifications.js that:

  • understand if the push notifications are supported by the browser.
  • Asks user consent
  • registers a ServiceWorker,
  • display a notification using the service worker

This script exports all the functions described above. To use these functions we can use a script like this: (This is just a sample, I’ll share the full code at the end of the article)

import {
isPushNotificationSupported,
sendNotification,
initializePushNotifications,
registerServiceWorker
} from "./push-notifications.js";
const pushNotificationSuported = isPushNotificationSupported();if (pushNotificationSuported) {registerServiceWorker();
initializePushNotifications().then(function(consent){
if(consent === 'granted') {
sendNotification();
}
});
}

Next, create the service worker file: sw.js. It will be, just for now, an empty file.

Push

As stated in the beginning, you need a server to send notifications to the user. We will call it the Push server.

Imagine this scenario: an e-commerce website wants to send a notification to users when a new product is released. These are the steps required:

  • The service worker creates a push notifications subscription.
  • This subscription creates a unique endpoint for the application/service worker/user. This endpoint is the one the push server will use to send push messages to.
  • The application sends the endpoint to the push server.
  • The push server saves the subscription of the user.
  • When a new product is released, the push server sends a push message/notification to the saved subscription endpoint.
  • The service worker registered by the application receives the notification and shows it to the user.

Create the push notification subscription.

to create a push notification subscription you use the method serviceWorker.pushManager.subscribe() (described here) that takes a parameter that represents the options. the properties of these options are:

  • userVisibleOnly: a boolean indicating that the returned push subscription will only be used for messages whose effect is made visible to the user.
  • applicationServerKey: an ECDSA (Elliptic Curve Digital Signature Algorithm) P-256 public key the push server will use to authenticate your application. Don’t worry, I’ll show you how to create it later on.

To access the service worker the safest method navigator.serviceWorker.ready that returns a promise that resolves the service worker registration. The promise is reloved when the service worker is ready. If we access the service worker in other ways, it could be not ready (maybe installing, or waiting).

Send the subscription to the server.

To send the subscription to the server there is no standard way, the important things to remember are:

  • The communication between the app and the push server must be secure because the subscription information is what is needed to send the notification. If stolen can be used to send unwanted notifications on behalf of the app.
  • when possible the subscription should be associated with a specific user, to send only relevant notifications.

This could be achieved with a simple fetch or with the HTTP client of the app when present. The object to send is a PushSubscription. Something like this

fetch(’push-server/user/{id}/push-subscription’, {
headers: { "content-type": "application/json;charset=UTF-8", "sec-fetch-mode": "cors" , <AUTH HEADERS>},
body: JSON.stringify(subscription),
method: "POST",
mode: "cors"
})

The push server that receives this call should validate and save the request, this example was made with express and NodeJS saving the subscription on a map in memory. In production, you want to save the subscription more persistently, such as a Database.

function handlePushNotificationSubscription(req, res) {
const subscriptionRequest = req.body;
const susbscriptionId = createHash(JSON.stringify(subscriptionRequest));
subscriptions[susbscriptionId] = subscriptionRequest;
res.status(201).json({ id: susbscriptionId });
}
app.post("/subscription", handlePushNotificationSubscription);

The full code is available here: https://github.com/Spyna/push-notification-demo/blob/master/back-end/src/subscriptionHandler.js#L18

Send the push notification

This is the work of the push server. There are some library and framework that implements a push server, I’m going to show you an example made in JavaScript with NodeJS. The code below uses a library called web-push

const webpush = require("web-push");// VAPID keys should only be generated only once.
// const vapidKeys = webpush.generateVAPIDKeys();
const vapidKeys = {
privateKey: "bdSiNzUhUP6piAxLH-tW88zfBlWWveIx0dAsDO66aVU",
publicKey: "BIN2Jc5Vmkmy-S3AUrcMlpKxJpLeVRAfu9WBqUbJ70SJOCWGCGXKY-Xzyh7HDr6KbRDGYHjqZ06OcS3BjD7uAm8"
};
webpush.setVapidDetails("example@yourdomain.org", vapidKeys.publicKey, vapidKeys.privateKey);webpush.sendNotification(pushSubscription, "The text of the notification")

Vapid means Voluntary Application Server Identification and is a web standard documented here.

The VAPID public key is the one shared with the web app ( the one which sends the notification subscription).

The method sendNotification accepts two parameters:

  • pushSubscription: a PushSubscription object, that is the one obtained from the service worker by the method: serviceWorker.pushManager.subscribe() as said earlier.
  • payload: the content of the push notification, which must be a string or a buffer. The example is a simple text, but it could be something more interesting like information about the notification to display or a reference to an API to call, in short, whatever you want to be inside the notification.
const payload = JSON.stringify({
title: "New Product Available ",
text: "HEY! Take a look at this brand new t-shirt!",
image: "/images/jason-leung-HM6TMmevbZQ-unsplash.jpg",
tag: "new-product",
url: "/new-product-jason-leung-HM6TMmevbZQ-unsplash.html"
});
const payload = `/product/<product-id>`;

Receive the pushed notification

I said earlier that you need a service worker to send the notification. Forget it! At this point, the push messages are sent by the push server.

You still need a service worker to receive push messages. You already have a service worker registered, but it does nothing. So we are going to add a listener to receive the push event:

In this script, we added a listener to the event push in the service worker. The event is an object of type PushMessageData containing the data sent from the push server and can be accessed as String, Object, and some other format using the methods event.data.text(), event.data.json() or blob().

Once you read the data from the notification event you can display the notification. Outside the service worker you called earlier the method: serviceWorkerRegistration.showNotification. Inside the service worker, you use self.registration.showNotification().

Click on the notification

The notification could be self-explicative and require no action, but usually, we want something to happen when the user clicks (or taps) the notification. You need to add a listener in the service worker for the event notificationclick

This snippet is part of the demo project, full code is available at https://github.com/Spyna/push-notification-demo/blob/master/front-end/public/sw.js#L19

In this code snippet, you added an event listener, that reads the event.notification.data property, and does something with it. In this example, the data is a URL (because we created it as a URL in the example before). Next, we open that URL in the browser window. Of course, you can do pretty much anything you want, playing around with the content of the notification and the service worker.

For example, if the user has already a notification you can avoid adding another one but modify the existing one with something like you have N messages to read.

Or if you consider these actions in the notification:

self.registration.showNotification("Your friend has posted a video", {
actions: [
{action: 'like', title: 'Like'},
{action: 'reply', title: ' Reply'}]
});

You can take different decisions based on the action type, which you can access in the property action of the event.

function handleNotification(event) {
event.notification.close();
if (event.action === 'like') {
// silently like the video;
}
else if (event.action === 'reply') {
// open the reply page }
else {
// do something else
}
}}
self.addEventListener('notificationclick', handleNotificaiton);

React.js Push notification

If you are interested in push notifications with React.js check out this article.

Advanced topics to keep in mind

This was an introduction to Web push notifications. If you read until this point, you should be a Push notification Champion. There are some topics to keep in mind when developing a production app that manages notifications, some of them are:

  • Handle permission denied to show notification from the user
  • update or unsubscribe from a notification subscription.
  • When the server pushes a notification, the client could be offline…
  • Notification payload encryption

--

--