DEV Community

Cover image for How To Make Push Notification Using Node.js and Service Worker
Weerayut Teja
Weerayut Teja

Posted on

How To Make Push Notification Using Node.js and Service Worker

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 this is the complete code of this tutorial.

Let's Get Started!

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

mkdir web-push-sample
cd web-push-sample
Enter fullscreen mode Exit fullscreen mode

Then install necessary libraries.

npm init 
npm i express body-parser web-push
Enter fullscreen mode Exit fullscreen mode

Next we will create start script by add node index.js to your start script

{
  "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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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

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
Enter fullscreen mode Exit fullscreen mode

It should returns something like this...

=======================================

Public Key:
BO4imRW5SYfMtEUyfwMrrxvzJjuoThJ1FNqiUX3Z0C93Ajdrhdy0rX5iwvGBWHffmH3nP-NhVsF5XXbnHxsUnrg

Private Key:
yI31gBBUlJYKj_7wZmPZsLGFklxNMVSk_9UVpWBXEHc

=======================================
Enter fullscreen mode Exit fullscreen mode

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

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);
});
Enter fullscreen mode Exit fullscreen mode

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.

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

    webpush.sendNotification(subscription, payload).catch(console.log);
Enter fullscreen mode Exit fullscreen mode

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

<!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>
Enter fullscreen mode Exit fullscreen mode

Then create the client.js and register our worker.

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

const publicVapidKey = "BOd2EQ8LTe3KAgMX9lWwTlHTRzv1Iantw50Mw6pUnsNr3pcxl8iglUs-YlQEQLo4UbJk9oyXs_BxgyAe0TCqKME";
Enter fullscreen mode Exit fullscreen mode

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.

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",
        }
    })
}
Enter fullscreen mode Exit fullscreen mode

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

if('serviceWorker' in navigator) {
    send().catch(console.log)
}
Enter fullscreen mode Exit fullscreen mode

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


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",
        }
    })
}
Enter fullscreen mode Exit fullscreen mode

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.

self.addEventListener('push', function(e) {
    const data = e.data.json();
    self.registration.showNotification(
        data.title,
        {
            body: data.body,
        }
    );
})
Enter fullscreen mode Exit fullscreen mode

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

Start Our Service

Start the app using the command below

npm start

Server started on port 5001
Enter fullscreen mode Exit fullscreen mode

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

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

Unblock Push Notification

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

Sample of notification

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).

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

Oldest comments (5)

Collapse
 
robiulman profile image
Robiul

if the browser tab is closed will the notification be popped up?

Collapse
 
wteja profile image
Weerayut Teja

@robiulman Yes, once user click accept allow button. We can send push notification anytime even the tab is closed.
As long as Browser's Background Process isn't terminated. The Service Worker will always listening for the new push notification.

Collapse
 
roygalaxy profile image
Roy Galaxy

On smartphones it does even if browser app is closed, but on pc it will only until browser is open.

Collapse
 
clouddark profile image
Cloud Dark

How to push notify for specify user?

Collapse
 
roygalaxy profile image
Roy Galaxy

From the front-end you can also send user's id along with subscription object then at the backend save their subscription object to your database with their user id. Then fetch it whenever you want to send notification to that particular user