Using Push Notifications with Service Workers and Node.js

Using Push Notifications with Service Workers and Node.js

In this article, we cover another feature that we can implement with the help of Service Workers – Push Notifications.

They come in handy if we need a fast channel of communicating with our users. We not only learn how to implement them in terms of code, but we also explore the User Experience side of it.

Introducing Push Notifications

The history of web development contains a lot of different ways of displaying popup messages for the users of our page. We came a long way from using the window.alert() and we have many new possibilities. With Push Notifications, we can push them locally when our application is opened. We can also do it from the server, even if our application closes. To do that, in this article, we use Service Workers and a simple server written in Node.js.

When and how to ask for permission

Before showing any notifications, we need to ask for permission first. We do it outside of any Service Worker, in our regular JavaScript code.

if (window.Notification) {
  Notification.requestPermission((status) => {
    console.log('Status of the request:', status);
  });
}

The Notification.requestPermission now also returns a promise in browsers like Chrome and Firefox, but Safari uses only a callback The code above opens a popup asking for permission. The choice of a user is stored so that he is not asked again. You can check out his decision in the Notification.permission parameter.

If the user changes his mind about notifications, he can easily change the settings by clicking an icon next to the address bar:

The big question in terms of User Experience is: should we ask our users for permissions right away? The chances are that when you find yourself in a situation like that, you instantly deny it. The above happens if we lack the context. Let the user know, why would you like to display some notifications for him.

Sometimes it might be obvious: for example when you are visiting some messaging application. Then, the user can safely guess that we want to notify him of any messages that he might receive.

Another example is an e-commerce application. Many users would consider notifications from a site like that to be just noise and spam if asked right away. On the other hand, if you display a permission popup when somebody makes a bid on an auction, the user might see some value in it and would want to be notified if he is not the highest bidder anymore. Remember that once someone denies permission to be presented with notifications, he needs to change it in the settings later explicitly: we can’t ask him again until then.

Sending Push Notifications locally

Once we got our permission, we can start displaying notifications. To do that, we need an active service worker.

index.js
if (window.Notification) {
  Notification.requestPermission(() => {
    if (Notification.permission === 'granted') {
      navigator.serviceWorker.register('./worker.js')
        .then((worker) => {
          worker.showNotification('Hello world!');
        });
    }
  });
}

For now, the worker.js file can be blank.> The navigator.serviceWorker.register('./worker.js') does not register a new worker, if it already exits. And just like that, we’ve got our first notification! By default, the showNotification function displays a basic notification. You can pass some more arguments to it, for example, to display an icon. It can also make some sounds or vibrations, depending on the platform.

We can also do some more fancy things with our notifications, for example, we can attach click events to the notifications.

worker.js
self.addEventListener('notificationclick', () => {
  console.log('Clicked!');
});

Please note that this is inside of the worker.js file One of the more interesting things that we can do is that we can define what buttons do we want on our notification popup. To do this, we use the actions property:

worker.showNotification('A new message!', {
  actions: [
    {
      action: 'show',
      title: 'Show it',
      icon: '/check.png'
    },
    {
      action: 'ignore',
      title: 'Ignore it',
      icon: '/delete.png'
    }
  ]
})

The design of the notification popup depends on the platform We can check which button is clicked in the notificationclick event and react accordingly.

worker.js
self.addEventListener('notificationclick', (event) => {
  if (!event.action) {
    console.log('No button clicked');
    return;
  }
  switch (event.action) {
    case 'show':
      console.log('User wants to see more');
      break;
    case 'ignore':
      console.log('User wants to ignore the notification');
      break;
    default:
      console.log(`The ${event.action} action is unknown`);
      break;
  }
});

Sending Push Notifications from the server

In this article, to send push notifications, we use a Node.js server.

An essential thing to keep in mind is that the data needs to be encrypted. An excellent tool for that is the web-push library. To authenticate we need a public and a private VAPID key: the web-push library can generate it for us. Let’s make a separate application that is going to serve as our backend.

npm install web-push
./node_modules/web-push/src/cli.js generate-vapid-keys

You can also install the web-push library globall instead of running it from the node_modules After running the commands above, we are presented with both the private and the public key. To place them in the env variables, we use the dotenv library.

npm install dotenv

If you would like to know more about env variables in Node.js, check out MongoDB, models and environment variables, that is a part of my Node.js Express series

require('dotenv/config');
const webpush = require('web-push');

const publicVapidKey = process.env.PUBLIC_VAPID_KEY;
const privateVapidKey = process.env.PRIVATE_VAPID_KEY;

webpush.setVapidDetails('mailto:[email protected]', publicVapidKey, privateVapidKey);

The first argument of the setVapidDetails function is called a subject. It provides a point of contact in case the push service needs to contact the sender.

Subscribing to the notifications

For our frontend to subscribe to the notifications, I’ll create an endpoint using Express.

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const webpush = require('web-push');

const app = express();
app.use(bodyParser.json());
app.use(cors({
  origin: 'http://localhost:8080'
}));

app.post('/subscribe', (req, res) => {
  const subscription = req.body;
  res.send(200);

  let i = 0;
  setInterval(() => {
    const payload = JSON.stringify({ title: `Hello!`, body: i++ });
    webpush.sendNotification(subscription, payload);
  }, 500);
});

app.listen(5000);

In this elementary example, I start spamming the user with multiple notifications for the sake of presentation.

Once the endpoint is ready, we can use it on the frontend.

<strong>index.js</strong>
const publicVapidKey = 'BEp878ZAJNHopeGksuSt5CtLL2iysV_uSskw7rvgbQIuqOC_UAlPEbbMLUtqfOdDi7ugqfeplwS7Is2dWJA7boc';
if (window.Notification) {
  Notification.requestPermission(() => {
    if (Notification.permission === 'granted') {
      getSubscriptionObject()
        .then(subscribe)
    }
  });
}
function getSubscriptionObject() {
  return navigator.serviceWorker.register('./worker.js')
    .then((worker) => {
      return worker.pushManager
        .subscribe({
          applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
        });
    });
}

In the function above we create a subscription object. We now need to send it to our endpoint.

function subscribe(subscription) {
  return fetch('http://localhost:5000/subscribe', {
    method: 'POST',
    body: JSON.stringify(subscription),
    headers: {
      'content-type': 'application/json'
    }
  });
}

Now the only thing left to do is to create the worker:

worker.js
self.addEventListener('push', event => {
  const data = event.data.json();
  self.registration.showNotification(data.title, {
    body: data.body,
  });
});

Every time we get the notification from the backend, the push event triggers. When that happens, we display the notifications.

The result

All of the code from above results in displaying multiple notifications, because the backend keeps sending them to us.

Even if we close the tab, the notifications keep coming, because they are independent.

Summary

In this article, we’ve covered Push Notifications. It included how and when to ask for permissions and how to display popups with notifications. Aside from that, we’ve also implemented a simple Node.js server that we can connect to and get notifications from. Thanks to that, we’ve learned yet another feature that Service Workers make possible.

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

Brave, Chrome, Firefox, Opera or Edge: Which is Better and Faster?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Top Node.js Development Companies and Expert NodeJS Developers

A thoroughly researched list of top NodeJS development companies with ratings & reviews to help hire the best Node.JS developers who provide development services and solutions across the world. List of Leading Node.js development Service Providers...

Hire Node JS Developer from Expert Node JS Development Company

NodeJS Development Company-Hire Node JS developer from the most prominent NodeJS development company, Mobiweb and get remarkable Node.js app development services.

For World Class Web Development Services in India visit RB Genie

Do you want excellent and world class web development services for your valuable projects? Contact **RB Genie **now, we have more than 8 years experienced team of web developers, which specializes in overall web design and website development...