Introduction:
Making a lucrative UI or smooth UX are no longer enough to keep your clients attracted to you now-a-days! You need more. You need User Engagement. Specially if you're selling something online, an e-commerce perhaps. And you want to inform your users about a big sale that's coming on a specific festival. How? You might think that if you had an app, you might send your users some push notifications. Which is true but not really feasible in many cases specially when you're new to business. and then you wonder..
What if a web application could send push notificatons? π€
Well lucky you because you're at the right spot!
In this article we'll...
- Do some basics.
- Create a simple web app with frontend and backend.
- Dive a bit deep on how it works.
- Some FAQs π€
So, let's go!
Environment Setup.
Fire up your vscode in a folder and create a js file. This will be the server part from where we'll send the notifications. Now create a new project using npm with the command
npm init -y
This will create a package.json file for you.
we need to install 2 npm packages.
- dotenv To store private and public VAPID key. (More on these later)
- web-push To actually send the notifications.
Install them with the npm install command.
The plan (Bird's Eye View):
There are two major players in this game.
ServiceWorkers and Web-Push
At first, we need to create to two keys which are called VAPID (Voluntary Application Server Identification) keys one is public and another is private (Asymmetric encryption/decryption but let's not dig deeper here).
The idea is that we register a serviceworker and use the subscription object to create a PushSubscription object using the public VAPID key.
We then send this object to the backend with a POST request. The server stores it. And in any event, the server uses the client's subscription object to send the notification.
In short...
Enough with the chit chat!
Project Structure:
root/
βββ client/
β βββ index.html
β βββ index.js
β βββ worker.js
βββ app.js
βββ .env
βββ package.json
βββ README.md
The Frontend:
HTML
We'll keep it as simple as possible. We're only interested on getting a push notification on a button click. No fancy design, no CSS. Just a single button in HTML.
ServiceWorker
We'll limit our serviceworker's role to just receiving push notifications and show it.
A notification needs to have three properties:
- Icon
- Title
- Body So, we'll design our payload object keeping this in mind. We'll use a fixed notification icon in this demonstration.
Nicely done!
The backend
Let's do the bare minimum in our backend part in our app.js file.
Such as:
- Including the necessary packages.
- Configuring the port.
- Pointing the static files directory.
- Configuring the environment variable. (We'll store our keys in the .env file btw)
- And an API endpoint to receive and store the client's info.
Great! now the crucial part!
Generating the VAPID keys
Run the following command on your terminal
npx web-push generate-vapid-keys
You'll get an output like this..
=======================================
Public Key:
<Public Key>
Private Key:
<Private Key>
=======================================
Copy the keys and store them in your .env file.
Now, we register the ServiceWorker.
Here's what we'd do:
- Register the ServiceWorker (worker.js in our case)
- Create Push Subscription. 2.1. Convert the public VAPID key to Uint8 Array. 2.2. Create the PushSubscription object
- Post it to the backend.
Like so..
I'm sure that you're wondering what's happening in the following code:
function convertToUnit8Array(base64str) {
const padding = '='.repeat((4 - (base64str.length % 4)) % 4)
const base64 = (base64str + padding).replace(/\-/g, '+').replace(/_/g, '/')
const rawData = atob(base64)
var outputArray = new Uint8Array(rawData.length)
for (let n = 0; n < rawData.length; n++) {
outputArray[n] = rawData.charCodeAt(n)
}
return outputArray
}
What it does is basically
- Pad the string with '=' character to make its length divisible by 4.
- Convert Base64URL β normal Base64
- Decode Base64 β raw binary string Basically, converting the human readable public key to machine readable byte array.
Now let's update our server code, shall we?
We'll configureour webpush with our public and private VAPID keys and an email address. For now, we're not providing any valid email.
We'll also implement a get API called /sendNofication to send a notification to the client.
Here's the updated code:
Great! We're now ready!
Fire up your server with the following command:
node app.js
And open your browser on the following URL:
Send Push Notifications Using Vanila JS! (Example with ExpressJS)
Making a lucrative UI or smooth UX are no longer enough to keep your clients attracted to you now-a-days! You need more. You need User Engagement. Specially if you're selling something online, an e-commerce perhaps. And you want to inform your users about a big sale that's coming on a specific festival. How? You might think that if you had an app, you might send your users some push notifications. Which is true but not really feasible in many cases specially when you're new to business. and then you wonder..
What if a web application could send push notificatons? π€
Well lucky you because you're at the right spot!
In this article we'll...
- Do some basics.
- Create a simple web app with frontend and backend.
- Dive a bit deep on how it works.
- Some FAQs π€
So, let's go!
Environment Setup.
Fire up your vscode in a folder and create a js file. This will be the server part from where we'll send the notifications. Now create a new project using npm with the command
npm init -y
This will create a package.json file for you.
we need to install 2 npm packages.
- dotenv To store private and public VAPID key. (More on these later)
- web-push To actually send the notifications.
Install them with the npm install command.
The plan (Bird's Eye View):
There are two major players in this game.
ServiceWorkers and Web-Push
At first, we need to create to two keys which are called VAPID (Voluntary Application Server Identification) keys one is public and another is private (Asymmetric encryption/decryption but let's not dig deeper here).
The idea is that we register a serviceworker and use the subscription object to create a PushSubscription object using the public VAPID key.
We then send this object to the backend with a POST request. The server stores it. And in any event, the server uses the client's subscription object to send the notification.
In short...
Enough with the chit chat!
Project Structure:
root/
βββ client/
β βββ index.html
β βββ index.js
β βββ worker.js
βββ app.js
βββ .env
βββ package.json
βββ README.md
The Frontend:
HTML
We'll keep it as simple as possible. We're only interested on getting a push notification on a button click. No fancy design, no CSS. Just a single button in HTML.
ServiceWorker
We'll limit our serviceworker's role to just receiving push notifications and show it.
A notification needs to have three properties:
- Icon
- Title
- Body So, we'll design our payload object keeping this in mind. We'll use a fixed notification icon in this demonstration.
Nicely done!
The backend
Let's do the bare minimum in our backend part in our app.js file.
Such as:
- Including the necessary packages.
- Configuring the port.
- Pointing the static files directory.
- Configuring the environment variable. (We'll store our keys in the .env file btw)
- And an API endpoint to receive and store the client's info.
Great! now the crucial part!
Generating the VAPID keys
Run the following command on your terminal
npx web-push generate-vapid-keys
You'll get an output like this..
=======================================
Public Key:
<Public Key>
Private Key:
<Private Key>
=======================================
Copy the keys and store them in your .env file.
Now, we register the ServiceWorker.
Here's what we'd do:
- Register the ServiceWorker (worker.js in our case)
- Create Push Subscription. 2.1. Convert the public VAPID key to Uint8 Array. 2.2. Create the PushSubscription object
- Post it to the backend.
Like so..
I'm sure that you're wondering what's happening in the following code:
function convertToUnit8Array(base64str) {
const padding = '='.repeat((4 - (base64str.length % 4)) % 4)
const base64 = (base64str + padding).replace(/\-/g, '+').replace(/_/g, '/')
const rawData = atob(base64)
var outputArray = new Uint8Array(rawData.length)
for (let n = 0; n < rawData.length; n++) {
outputArray[n] = rawData.charCodeAt(n)
}
return outputArray
}
What it does is basically
- Pad the string with '=' character to make its length divisible by 4.
- Convert Base64URL β normal Base64
- Decode Base64 β raw binary string Basically, converting the human readable public key to machine readable byte array.
Now let's update our server code, shall we?
We'll configureour webpush with our public and private VAPID keys and an email address. For now, we're not providing any valid email.
We'll also implement a get API called /sendNofication to send a notification to the client.
Here's the updated code:
Great! We're now ready!
Fire up your server with the following command:
node app.js
And open your browser on the following URL:
localhost:4000
And follow me!
BOOM! We've implemented push notifications!

FAQs:
- Is it scalable?
HELL YEAH πͺ It's the push notification services that does the main heavy lifting. Not you! (E.g. Google, Firefox, Apple etc depending on the browser). If you inspect the PushSubscription object, you'll find a property called
endpoint. When your server wants to send you a push notification, it basically becomes a REST API client and calls API mentioned in the endpoint property.- Do you have to manage any states? You might think that since it's real-time, you'll have to store some state in the server like WebSocket. Luckily that's not the case. You only need to store the user's subscription data in the database.
Conclusion:
So, we've implemented push notifications using vanilla js! Please let me know how I can improve this article.
Here's the entire codebase btw if you're interested:
Git Repository



Top comments (0)