How To Make Push Notification Using Node.js and Service Worker
Tags: javascript, serviceworker, node, push
Published: 2022-03-20 05:08:11

---
title: How To Make Push Notification Using Node.js and Service Worker
published: true
description: Using Node.js, Service Worker, and the Web Push library, create a push notification-like mobile application.
tags: javascript, serviceworker, nodejs, push
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbb7i449ovroqdihf5kr.png
---

We might find push notifications on mobile really useful because a web server can communicate directly with the app when it wants.

But app users need to grant permission to receive the push notifications first.

We can simply create the push notification using only `Node.js`, `Express.js` and `Web-Push`.

## Sample Code

You can find [sample code here](https://github.com/wteja/nodejs-web-push-sample) this is the complete code of this tutorial.

## Let's Get Started!

Let's create a directory that contains our Express.js app.

```bash
mkdir web-push-sample
cd web-push-sample
```

Then install necessary libraries.

```bash
npm init 
npm i express body-parser web-push
```
Next we will create **start** script by add **node index.js** to your start script

```json
{
  "name": "web-push-sample",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "body-parser": "^1.19.2",
    "express": "^4.17.3",
    "web-push": "^3.4.5"
  },
  "scripts": {
    "start": "node index.js"
  }
}
```

## Generate VAPID Keys for push notifications

VAPID, which stands for *Voluntary Application Server Identification* is a new way to send and receive website push notifications. Your VAPID keys allow you to send web push campaigns without having to send them through a service like Firebase Cloud Messaging (or FCM). Instead, the application server can voluntarily identify itself with your web push provider.

I have two recommended ways to create VAPID Keys

### 1) Generate from [vapidkeys.com](https://vapidkeys.com)

Just go through the website, enter the email address. It is used to give details about the sender. Use it as an identifier.

Then click the "generate" button. You should get the JSON object that contains **subject** which is your given email address.

And you will get both public and private keys.

![VAPID Keys generated from vapidkeys.com](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/24m907tihzrp8k6fdfiv.png)

Really easy right?

### 2) Generate by Command Line

If you don't want to use the online services, you can generate it through the command line. So open your terminal and enter this command.

```
./node_modules/.bin/web-push generate-vapid-keys
```

It should returns something like this...

```
=======================================

Public Key:
BO4imRW5SYfMtEUyfwMrrxvzJjuoThJ1FNqiUX3Z0C93Ajdrhdy0rX5iwvGBWHffmH3nP-NhVsF5XXbnHxsUnrg

Private Key:
yI31gBBUlJYKj_7wZmPZsLGFklxNMVSk_9UVpWBXEHc

=======================================
```

## Setup the Subscription Route

Next you will need to create express app and setup the route for allow client to subscribe to your push notification

```javascript
const express = require('express');
const webpush = require('web-push');
const bodyParser = require('body-parser');
const path = require('path');

// Create express app.
const app = express();

// Use body parser which we will use to parse request body that sending from client.
app.use(bodyParser.json());

// We will store our client files in ./client directory.
app.use(express.static(path.join(__dirname, "client")))

const publicVapidKey = "BOd2EQ8LTe3KAgMX9lWwTlHTRzv1Iantw50Mw6pUnsNr3pcxl8iglUs-YlQEQLo4UbJk9oyXs_BxgyAe0TCqKME";

const privateVapidKey = "4AoSsRHFaHv0Fupd2NRtrungJF2jkqgccTu-WEc781w";

// Setup the public and private VAPID keys to web-push library.
webpush.setVapidDetails("mailto:test@test.com", publicVapidKey, privateVapidKey);

// Create route for allow client to subscribe to push notification.
app.post('/subscribe', (req, res) => {
    const subscription = req.body;
    res.status(201).json({});
    const payload = JSON.stringify({ title: "Hello World", body: "This is your first push notification" });

    webpush.sendNotification(subscription, payload).catch(console.log);
})

const PORT = 5001;

app.listen(PORT, () => {
    console.log("Server started on port " + PORT);
});
```

Inside **subscribe** route we will also start to sending first push notification but it need to pass the string as the value.
**If you want to sending the JSON object, you will need to send as a string, and parse it on client side.**

Below are the example on sending push notification to the client.

```javascript
const payload = JSON.stringify({ title: "Hello World", body: "This is your first push notification" });

    webpush.sendNotification(subscription, payload).catch(console.log);
```

### Create Service Worker to Consume Push Notification

We will need 3 files in **client** directory to start to consume our push notifications

1) index.html - Our landing page that will make use our main javascript file.
2) client.js - Our main JavaScript file which will register our service worker.
3) worker.js - Our Service Worker JavaScript file which will handle the push notifications.

Let's create **index.html** file

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Push Notification Using Web-Push</title>
</head>
<body>
    <h1>Push Notification Using Web-Push</h1>

    <script src="./client.js"></script>
</body>
</html>
```

Then create the **client.js** and register our worker.

We will need to copy our public keys and store it to some variable.

```javascript
const publicVapidKey = "BOd2EQ8LTe3KAgMX9lWwTlHTRzv1Iantw50Mw6pUnsNr3pcxl8iglUs-YlQEQLo4UbJk9oyXs_BxgyAe0TCqKME";
```

Then we write the function to subscribe the push notification.
We create the subscription object then send the **POST** request to our API endpoint and attach that subscription object as the payload.

```javascript
async function registerServiceWorker() {
    const register = await navigator.serviceWorker.register('./worker.js', {
        scope: '/'
    });

    const subscription = await register.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: publicVapidKey,
    });

    await fetch("/subscribe", {
        method: "POST",
        body: JSON.stringify(subscription),
        headers: {
            "Content-Type": "application/json",
        }
    })
}
```

Finally we make use this function after we check that our current web browser supports **Service Worker**

```javascript
if('serviceWorker' in navigator) {
    send().catch(console.log)
}
``` 

So the final look of **client.js** will be like this

```javascript

async function registerServiceWorker() {
    const register = await navigator.serviceWorker.register('./worker.js', {
        scope: '/'
    });

    const subscription = await register.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: publicVapidKey,
    });

    await fetch("/subscribe", {
        method: "POST",
        body: JSON.stringify(subscription),
        headers: {
            "Content-Type": "application/json",
        }
    })
}
```

### Create Service Worker

Create **worker.js** we will add event listener inside this file. When we show the notification, we will need to pass title and body as the parameters.

We add event listener to **push** event. Parse data as a **JSON object**. Then can **self.registration.showNotification** method then pass title and body.

```javascript
self.addEventListener('push', function(e) {
    const data = e.data.json();
    self.registration.showNotification(
        data.title,
        {
            body: data.body,
        }
    );
})
```

That is all about it!

## Let's test notification service.

In this example I will use Google Chrome for testing.

Please make sure that you allow notification on Google Chrome.
If you using Mac it might disabled on the first time. And you will need to enable it.

Go to Notification Center then find the Google Chrome and allow notification. Make sure the **Allow Notification** enabled.

![Enable notification for Google Chrome on Mac](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hhst3iyfi0zizq7og9n4.png)

## Start Our Service

Start the app using the command below

```bash
npm start

Server started on port 5001
```

Open your browser and navigate to **http://localhost:5001**

It should show the push notification permission request dialog. Just click allow.

![Push Notification Subscription Dialog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9eac7gsdzyim699ujo1.png)

but in some case it might not show. But have the **lock** icon in navigation bar. Clicking on it and allow 

![Unblock Push Notification](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvroxabwn5xzzvyslgnu.png)

Then you should get the first notification. (If not refresh the page once)

![Sample of notification](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nbdwq591me83ofg961oz.png)

Congratulations! You did it. That's your first push notification.

## Tip: Simulate More Push Notification

We can simulate more push notification that we can receive. Let assume that you still browsing through Google Chrome Right now you can open the **Google Chrome DevTool** and go to **Application tab** navigate to **ServiceWorker** menu.

You will see your **worker.js** file has been registered there.

Scroll down a bit, you can see the **push** text box.

Put some test JSON object there and press **push** button then you can see more notifications. Just enjoy with it.

That's means if you send any more push notification from your web server. It will also appeared on user's browsers as well (as long as they choose to subscribe to your push notification)

## Conclusion

We can easily create our push notifications using **web-push** without using a 3rd party service like [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging).

By using the new way to provide self hosted push notifications called VAPID (Voluntary Application Server Identity)

Then we create and install **Service Worker** that interacts after receiving the new push notifications. Showing notification is the choice. The key point is that after you subscribe to web-push successfully, you can receive push data anytime. You might use that information in ways you can't imagine.

## Resources

- [Web Push GitHub Repository](https://github.com/web-push-libs/web-push)
- [VAPID Keys Generator](https://vapidkeys.com/)
- [Service Worker](https://developer.chrome.com/docs/workbox/service-worker-overview/)
- [Sample code](https://github.com/wteja/nodejs-web-push-sample)