push-notifications-server-codelab-complete (source) to see the complete code.
This codelab is known to work with the following operating system and browser combinations:
This codelab is known to not work with the following operating systems (or operating system and browser combinations):
You don't have to use any of these technologies to implement push notifications. We chose these technologies because they provide a reliable codelab experience.
The code editor that you see to the right of these instructions will be called the Glitch UI throughout this codelab.
Before you can get push notifications working, you need to set up your server and client with authentication keys. See Sign your web push protocol requests to learn why.
npx web-push generate-vapid-keys
. Copy the private key and public key values..env
and update VAPID_PUBLIC_KEY
and VAPID_PRIVATE_KEY
. Set VAPID_SUBJECT
to mailto:test@test.test
. All of these values should be wrapped in double quotes. After making your updates, your .env
file should look similar to this:VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
Environment variable values (the stuff in .env
) are unique to a single Glitch project. If you remix your project, the values in .env
won't get copied over.
public/index.js
.VAPID_PUBLIC_KEY_VALUE_HERE
with the value of your public key.Your client handles most of the subscription process. The main things your server needs to do are save new push notification subscriptions and delete old subscriptions. These subscriptions are what enable you to push messages to clients in the future. See Subscribe the client to push notifications for more context about the subscription process.
Key Term: We'll refer to the tab that you just opened as the app tab.
Service worker registered. Scope: https://desert-cactus-sunset.glitch.me/
Service worker subscribed to push. Endpoint: https://fcm.googleapis.com/fcm/send/…
The endpoint URL changes depending on what browser you're using. For example, on Firefox the URL starts with https://updates.push.services.mozilla.com/…
.
/add-subscription
followed by some data. /add-subscription
is the URL that the client sends a POST request to when it wants to subscribe to push notifications. The data that follows is the client's subscription information that you need to save.server.js
./add-subscription
route handler logic with the following code:app.post('/add-subscription', (request, response) => {
console.log('/add-subscription');
console.log(request.body);
console.log(`Subscribing ${request.body.endpoint}`);
db.get('subscriptions')
.push(request.body)
.write();
response.sendStatus(200);
});
The database writes to .data/db.json
. To inspect this file in Glitch, click Tools, then click Terminal, then run cat .data/db.json
in the Terminal. .data/db.json
is deleted every time that you edit your app. This is because Glitch runs the start
script in package.json
every time you edit your app, and that script includes a call to rm .data/db.json
.
/remove-subscription
followed by the client's subscription information./remove-subscription
route handler logic with the following code:app.post('/remove-subscription', (request, response) => {
console.log('/remove-subscription');
console.log(request.body);
console.log(`Unsubscribing ${request.body.endpoint}`);
db.get('subscriptions')
.remove({endpoint: request.body.endpoint})
.write();
response.sendStatus(200);
});
As explained in Send a push message, your server doesn't actually send the push messages directly to clients. Rather, it relies on a push service to do that. Your server basically just kicks off the process of pushing messages to clients by making web service requests (web push protocol requests) to a web service (the push service) owned by the browser vendor that your user uses.
/notify-me
route handler logic with the following code:app.post('/notify-me', (request, response) => {
console.log('/notify-me');
console.log(request.body);
console.log(`Notifying ${request.body.endpoint}`);
const subscription =
db.get('subscriptions').find({endpoint: request.body.endpoint}).value();
sendNotifications([subscription]);
response.sendStatus(200);
});
sendNotifications()
function with the following code:function sendNotifications(subscriptions) {
// TODO
// Create the notification content.
const notification = JSON.stringify({
title: "Hello, Notifications!",
options: {
body: `ID: ${Math.floor(Math.random() * 100)}`
}
});
// Customize how the push service should attempt to deliver the push message.
// And provide authentication information.
const options = {
TTL: 10000,
vapidDetails: vapidDetails
};
// Send a push message to each client specified in the subscriptions array.
subscriptions.forEach(subscription => {
const endpoint = subscription.endpoint;
const id = endpoint.substr((endpoint.length - 8), endpoint.length);
webpush.sendNotification(subscription, notification, options)
.then(result => {
console.log(`Endpoint ID: ${id}`);
console.log(`Result: ${result.statusCode}`);
})
.catch(error => {
console.log(`Endpoint ID: ${id}`);
console.log(`Error: ${error} `);
});
});
}
/notify-all
route handler logic with the following code:app.post('/notify-all', (request, response) => {
console.log('/notify-all');
response.sendStatus(200);
console.log('Notifying all subscribers');
const subscriptions =
db.get('subscriptions').cloneDeep().value();
if (subscriptions.length > 0) {
sendNotifications(subscriptions);
response.sendStatus(200);
} else {
response.sendStatus(409);
}
});
Hello, Notifications!
and the body should be ID: <ID>
where <ID>
is a random number.