push notification next js with mongodb (no third party)
Web Push Notification Full Stack
Application With Node Js Restful API
![]()
Let me tell you first, why Web Push Notification is
essential for any web application.
It's helps in broadcasting short messages to the subscriber of the web
application and as well as help to support in user engagement with your
application to notify the every subscribers, It is really hard to any
organisation send email notification for a short messaging event loop or
broadcast messages like offers and updates of short messages and it also
increases the cost of server operations so the solution is Web Push Notification .it help your the maximum post reachability and
getting more consumer engagement on web application.
let me tell you how it's works.

service worker is main key ingrediant and knight of this
feature which install in client broswer and run indenpendently as application
after intalling in browser as service worker which regularly a send a query to
the provider server and ask for any new event happening and than respond to the
client if any new event is happening in the server it popup a message like

click on allow button service worker start to install in
client browser and send a promise request for subscriber with Public
VAPID key and check on server for whether user is already subscribe
notification or not if it's already subscribe it sent back request with false
statement otherwise server sent a true request.
and done that's it.
Now let's Comes to the Coding section and how to implement this feature in your
application without using third party paid services and use as long as you.
Step 1
Need Prequest which are listed below if you don't have in
your System.
Prequiests:
1.
IDE Visual Studio Code
2.
Nodejs
3.
Git
4.
MongoDB
5.
Postman
Now Let's Beigin with next Step
Step 2
open your IDE Visual Studio Code
and than run command in integrated terminal with your IDE
git init
than add the all fields or skip as you want
and than run command again to intall all the dependacy with
npm install
express web-push body-parser mongoose q --save
and hit the Enter and wait for install all the dependency
will install correclty in your project than run again run command for creat new
application running file in same project folder by
touch
server.js
and again need to create three folders in same project
directory by commands as below
mkdir config
cd config
touch keys.js
touch
keys_prod.js
touch
keys_dev.js
mkdir model
cd model
touch
subscribers_model.js
mkdir router
cd router
touch push.js
touch
subscriber.js
touch index.js
now all the essential folders and file are created and in
this project we move to next coding parts in next step.
Step 3
The file structure of this project is as below
|
|
|________________________./config
| |
| |____keys_prod.js
| |____keys_dev.js
| |____keys.js
|
|________________________./public
| |
| |____index.html
| |____sw.js
| |____app.js
|
|________________________./model
| |
| |____subscribers_model.js
|
|________________________./router
| |
| |____push.js
| |____subscribe.js
|
|___________________________server.js
now start with create database model for mongodb
database. so now i am using Mongoose ODM ORM library for MongoDB which
is already installed in project
const mongoose
= require('mongoose');
const Schema =
mongoose.Schema;
const
SubscriberSchema = new Schema({
endpoint: String,
keys: Schema.Types.Mixed,
createDate: {
type: Date,
default: Date.now
}
});
mongoose.model('subscribers',
SubscriberSchema, 'subscribers');
so now let's come to the config file
cd config
and than open the keys.js file which is already created
in this folder
if
(process.env.NODE_ENV === 'production') {
module.exports = require('./keys_prod');
} else {
module.exports = require('./keys_dev');
}
and update your keys.js file with this code , actually
this code provide smart switch database authentication address between
production and development application.
Before update the keys_prod.js and keys_dev.js file generate the VAPID keys for
client device browser and between the server running application.
by using this command
./node_modules/.bin/web-push
generate-vapid-keys
you will see a two keys are generated one is private and
another one is public key
which is show in below.
copy both the keys and paste in to the keys_dev.js or on
production enviroment server config.
module.exports
= {
//i used mlab
database for fast and realiable pace development enviroment
mongoURI:
'mongodb://web-push:webpush123@ds213053.mlab.com:13053/web-push',
privateKey:
'ayTIBl3f0gcI-koFq-ZXPxSR4qicC0GcMNHA1dpHaj0' || process.env.VAPID_PRIVATE_KEY,
publicKey: 'BK3Q7j8fcGFws03RiU5XakzDJ7KGEiRhdIX2H5U8eNmhhkdHT_j0_SD09KL96aFZOH_bsjr3uRuQPTd77SRP3DI'
|| process.env.VAPID_PUBLIC_KEY
}
process.env.VAPID_PUBLIC_KEY or process.env.VAPID_PRIVATE_KEY understand
as production server running environment configuration.
Note: Please Make sure you always
use or generate your own VAPID keys for your application for more secure your
application and make sure your private keys is not expose on the web.
so now all the important application structure setting is
done now start coding in server.js which is exist on top of
the project folder
const express
= require('express');
const path =
require('path');
const mongoose
= require('mongoose');
const
bodyParser = require('body-parser');
require('./model/subscribers_model');
// Load Routes
const index =
require('./router');
// subscriber
route load push
const push =
require('./router/push');
// subscriber
route load
const
subscribe = require('./router/subscribe');
// Load Keys
const keys =
require('./config/keys');
//Handlebars
Helpers
mongoose.Promise
= global.Promise;
// Mongoose
Connect
mongoose.connect(keys.mongoURI,
{
useMongoClient: true
})
.then(() => console.log('MongoDB
Connected'))
.catch(err => console.log(err));
//Create
Express middleware
const app =
express();
app.set('trust
proxy', true);
// parse
application/json
app.use(bodyParser.json());
// parse
application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({
extended: true
}));
// Set static
folder
app.use(express.static(path.join(__dirname,
'public')));
//
app.set('views', __dirname + '/public/js');
// Set global
vars
app.use((req,
res, next) => {
res.locals.user = req.user || null;
next();
});
// Use Routes
app.use('/',
index);
app.use('/subscribe',
subscribe);
app.use('/push',
push);
// catch 404
and forward to error handler
app.use(function
(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error
handlers
// development
error handler
// will print
stacktrace
if
(app.get('env') === 'development') {
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production
error handler
// no
stacktraces leaked to user
app.use(function
(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
const port =
process.env.PORT || 3000;
app.listen(port,
() => {
console.log(`Server started on port
${port}`);
});
and now come to the folder router first
start with subscribe.js which is already created by command. open this file in
new tab and than paste this code in your subscribe.js file
const express
= require('express');
const router =
express.Router();
const mongoose
= require('mongoose');
const
Subscription = mongoose.model('subscribers');
//Post route
of subscribe url is as http://host:3000/subscribe
router.post('/',
(req, res) => {
const subscriptionModel = new
Subscription(req.body);
subscriptionModel.save((err, subscription)
=> {
if (err) {
console.error(`Error occurred while
saving subscription. Err: ${err}`);
res.status(500).json({
error: 'Technical error
occurred'
});
} else {
res.json({
data: 'Subscription saved.'
});
}
});
});
// fixed the
error get request for this route with a meaningful callback
router.get('/',
(req, res) => {
res.json({
data: 'Invalid Request Bad'
});
});
module.exports
= router;
save the changes and move to next file push.js and
paste this code in your already created push.js file by
command line
const express
= require('express');
const router =
express.Router();
const mongoose
= require('mongoose');
const
Subscription = mongoose.model('subscribers');
const q =
require('q');
const webPush
= require('web-push');
const keys =
require('./../config/keys');
//Post route
of push url is as http://host:3000/push
router.post('/',
(req, res) => {
const payload = {
title: req.body.title,
message: req.body.message,
url: req.body.url,
ttl: req.body.ttl,
icon: req.body.icon,
image: req.body.image,
badge: req.body.badge,
tag: req.body.tag
};
Subscription.find({}, (err, subscriptions)
=> {
if (err) {
console.error(`Error occurred while
getting subscriptions`);
res.status(500).json({
error: 'Technical error
occurred'
});
} else {
let parallelSubscriptionCalls =
subscriptions.map((subscription) => {
return new Promise((resolve,
reject) => {
const pushSubscription = {
endpoint:
subscription.endpoint,
keys: {
p256dh:
subscription.keys.p256dh,
auth:
subscription.keys.auth
}
};
const pushPayload =
JSON.stringify(payload);
const pushOptions = {
vapidDetails: {
subject:
"http://example.com",
privateKey:
keys.privateKey,
publicKey:
keys.publicKey
},
TTL: payload.ttl,
headers: {}
};
webPush.sendNotification(
pushSubscription,
pushPayload,
pushOptions
).then((value) => {
resolve({
status: true,
endpoint:
subscription.endpoint,
data: value
});
}).catch((err) => {
reject({
status: false,
endpoint:
subscription.endpoint,
data: err
});
});
});
});
q.allSettled(parallelSubscriptionCalls).then((pushResults) => {
console.info(pushResults);
});
res.json({
data: 'Push triggered'
});
}
});
});
// fixed the
error get request for this route with a meaningful callback
router.get('/',
(req, res) => {
res.json({
data: 'Invalid Request Bad'
});
});
module.exports
= router;
again make sure save this code changes in your push.js file
with this code now again move to the index.js file and update
the code with this below code
const express
= require('express');
const router =
express.Router();
router.get('/',
(req, res) => {
res.locals.metaTags = {
title: 'web-push-api',
description: 'Web Push Notification
Full Stack Application With Node Js Restful API',
keywords: 'Web Push Notification Full
Stack Application With Node Js Restful API',
generator: '0.0.0.1',
author: 'Saurabh Kashyap'
};
res.json({
status: 'ok',
message: 'Server is running'
});
});
module.exports
= router;
and save the changes in server.js file with above code
in server.js file and run command hit this run command
node server.js
please make sure you will see these messages after
hitting this command.
press again close the application after checking your
application is running correct.
so now server side running application code is done.
Now Let's Begin with next Step
Step 4
creat a new folder in with public name and create file
with these commands as below
mkdir public
cd public
touch
index.html
touch sw.js
touch app.js
now lets beign the basic html code in index.html file
<!DOCTYPE
html>
<html
lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<meta
http-equiv="X-UA-Compatible" content="ie=edge">
<title>Web-Push Application with
Restful Api</title>
</head>
<body>
<h1>This is a web-push notification
example</h1>
<!-- call service worker for register and
send subscribe request to the server with javascript -->
<script
src="app.js"></script>
</body>
</html>
save this code and move to next file app.js where
service worker browser check and register service worker in browser and also
send a ajax request to the application API http://host:3000/subscribe for
subscribe the service in client browser.
let
isSubscribed = false;
let
swRegistration = null;
let
applicationKey = "put_your_public_key_here";
// Url
Encription
function
urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 -
base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new
Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i)
{
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// Installing
service worker
if
('serviceWorker' in navigator && 'PushManager' in window) {
console.log('Service Worker and Push is
supported');
navigator.serviceWorker.register('sw.js')
.then(function (swReg) {
console.log('service worker
registered');
swRegistration = swReg;
swRegistration.pushManager.getSubscription()
.then(function (subscription) {
isSubscribed =
!(subscription === null);
if (isSubscribed) {
console.log('User is
subscribed');
} else {
swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(applicationKey)
})
.then(function
(subscription) {
console.log(subscription);
console.log('User is subscribed');
saveSubscription(subscription);
isSubscribed =
true;
})
.catch(function
(err) {
console.log('Failed to subscribe user: ', err);
})
}
})
})
.catch(function (error) {
console.error('Service Worker
Error', error);
});
} else {
console.warn('Push messaging is not
supported');
}
// Send
request to database for add new subscriber
function
saveSubscription(subscription) {
let xmlHttp = new XMLHttpRequest();
//put here API address
xmlHttp.open("POST",
"/subscribe");
xmlHttp.setRequestHeader("Content-Type",
"application/json;charset=UTF-8");
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState != 4) return;
if (xmlHttp.status != 200 &&
xmlHttp.status != 304) {
console.log('HTTP error ' +
xmlHttp.status, null);
} else {
console.log("User subscribed
to server");
}
};
xmlHttp.send(JSON.stringify(subscription));
}
and now save the all files and start coding for a service
worker let's begin now
let
notificationUrl = '';
//notification
registered feature for getting update automatically from server api
self.addEventListener('push',
function (event) {
console.log('Push received: ', event);
let _data = event.data ?
JSON.parse(event.data.text()) : {};
notificationUrl = _data.url;
event.waitUntil(
self.registration.showNotification(_data.title,
{
body: _data.message,
icon: _data.icon,
tag: _data.tag
})
);
});
//notification
url redirect event click
self.addEventListener('notificationclick',
function (event) {
event.notification.close();
event.waitUntil(
clients.matchAll({
type: "window"
})
.then(function (clientList) {
if (clients.openWindow) {
return
clients.openWindow(notificationUrl);
}
})
);
});
nad save the all code . YEAH..!! done. so now we have to
check wheater is it working or not. so again run command in your terminal
node server.js
open url: http://localhot:3000 in your
browser now
after click on allo you see message like in your browser
console
and save the all code . YEAH..!! done. so now we have to
check whether is it working or not. so again run command in your terminal
node server.js
open url: http://localhot:3000 in your
browser now
after click on allow you see message like in your browser
console
and open postman with for send push
message to subscibers.
push send example
{
"title": "StoryBook",
"message": "welcome
friends",
"url": "https://new-storybook.herokuapp.com",
"ttl": 36000,
"icon":
"https://cdn3.iconfinder.com/data/icons/happy-x-mas/501/santa15-128.png",
"badge":
"https://cdn3.iconfinder.com/data/icons/happy-x-mas/501/santa15-128.png",
"data":"Hello New
World",
"tag": "Book Story"
}