Introdução
Neste tutorial vamos aprender a enviar Push Notification para o Browser usando Node.js, irei utilizar também o React com Vite, mas poderia ser qualquer outra coisa, pois vamos utilizar de API's nativas da WEB para isso.
Será abordado o uso da Notification API para solicitarmos a permissão para o usuário, PushManager API para receber notificações de servidores de terceiros, web-push para fazer o envio da notificação no lado servidor, e por fim o Service Worker, para lidar com a notificação em Background.
Criando Back-end
Antes de tudo, precisamos criar nosso servidor em Node.js para fazer o envio dessas notificações, vou utilizar o Express para isso, mas poderia ser com qualquer outro, como o Fastify etc.
Instalando pacotes necessários e configurando script
$ mkdir server
$ cd server
$ npm init -y
$ npm i express cors web-push
$ npm i typescript tsx @types/express @types/cors @types/node @types/web-push -D
$ npx tsc --init
Adicione no seu package.json o script de dev:
"scripts": {
"dev": "tsx watch src/server.ts"
}
Criando servidor
Crie um arquivo dentro da pasta src chamado server.ts e adicione o código abaixo:
import express from 'express';
import cors from 'cors';
import WebPush from 'web-push';
const app = express();
app.use(express.json());
app.use(cors());
// WebPush
// console.log(WebPush.generateVAPIDKeys());
const publicKey = 'DASdasdjk1hdhajshdashdgadaskdhaj...';
const privateKey = 'dsaduiw18eudasd1d9sdd3wddasdasa...';
WebPush.setVapidDetails('http://localhost:3333', publicKey, privateKey);
// Routes
interface ISubscription {
subscription: {
endpoint: string;
keys: {
p256dh: string;
auth: string;
};
};
}
app.get('/notification/push/public_key', (request, response) => {
return response.json({ publicKey });
});
app.post('/notification/push/register', (request, response) => {
console.log(request.body);
return response.sendStatus(201);
});
app.post('/notification/push/send', async (request, response) => {
const { subscription } = request.body as ISubscription;
WebPush.sendNotification(
subscription,
JSON.stringify({
icon: 'your-icon-link.png',
title: "'Your title',"
body: 'Content of your message',
imageUrl: 'your-image-link.png'
}),
);
return response.sendStatus(201);
});
app.listen(3333, () => console.log('SERVER IS RUNNING AT :3333'));
Bom de Back-end é só isso, para você gerar sua publicKey e privateKey é necessário rodar o código console.log(WebPush.generateVAPIDKeys()); somente uma vez, com isso ele vai te retornar suas chaves, ai basta você substituir essas fictícias que coloquei pelas as suas.
Sobre as rotas do servidor:
/notification/push/public_key: Essa rota é responsável por enviar nossa
publicKeypara o Front-end./notification/push/register: Essa rota serviria para você salvar no Banco de Dados por exemplo a
subscriptionque o Front-end iria enviar, e com essa informação você poderia enviar a notificação depois, mas para não estender muito não irei abordar essa parte de salvar em algum Banco de Dados./notification/push/send: Essa rota serve para fazer o envio da notificação, nela estou recebendo a
subscriptionno body, pois é necessário para o envio da notificação, mas caso você tenha persistido essas subscriptions no Banco de Dados, você poderia substituir o recebimento dela nessa rota, e realizar a consulta dessas subscriptions existentes.
Preparando nosso Front-end
Criando o Service Worker
Vamos criar um arquivo chamado service-worker.ts dentro da pasta public pois esse arquivo precisa estar disponível publicamente.
Estou utilizando
.tspois TypeScript é vida ❤️, mas se você quiser pode usar.jse remover as tipagens que irei adicionar, de qualquer forma se você usar com TypeScript, vai precisar converter para JavaScript no final, mas fique tranquilo irei mostrar como posteriormente.
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />
interface INotificationPayload {
icon: string;
title: string;
body: string;
imageUrl?: string;
}
const sw = self as unknown as ServiceWorkerGlobalScope & typeof globalThis;
sw.addEventListener('push', (event) => {
const notificationPayload = event.data?.json() as INotificationPayload;
event.waitUntil(
sw.registration.showNotification(notificationPayload.title, {
icon: notificationPayload.icon,
body: notificationPayload.body,
image: notificationPayload.imageUrl
}),
);
});
O código acima é bem simples, basicamente adicionamos um
listenerchamadopush, com isso, quando for enviado alguma notificação vamos ficar sabendo, e teremos acesso ao evento que foi disparado, com esse evento conseguimos obter opayloadque é os dados que foram enviados, e com esses dados conseguimos exibir de fato a notificação para o usuário.Você pode explorar mais atributos, que podem ser passados dentro desse objeto de configuração da função
showNotification, por meio da documentação oficial.
Solicitando permissão
Agora precisamos solicitar as permissões para o usuário, ele precisa de fato autorizar o recebimento dessas notificações para estar chegando para ele.
Você pode implementar o código abaixo no arquivo principal do seu projeto, para que quando o mesmo for iniciado chame a função abaixo.
function notifyMe() {
if (!('Notification' in window)) {
alert('This browser does not support desktop notification');
} else if (Notification.permission === 'granted') {
handleServiceWorker();
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
handleServiceWorker();
}
});
}
}
notifyMe();
Basicamente na função acima, realizamos uma verificação para checar se temos acesso a
Notification API, em seguida verificamos se já temos acesso garantido a notificação, se já tivermos só executamos a funçãohandleServiceWorkerque criaremos depois, por fim se ainda não tivermos concedido permissão para a notificação, solicitamos ao usuário para que garanta o acesso.
Criando função handleServiceWorker
Essa função vai realizar o registro do nosso Service Worker, e após ser realizado o registro com sucesso, chamamos a função getSubscription do pushManager, pois ela recupera uma assinatura push existente e retorna seus detalhes, caso não encontrar retorna null.
Quando não temos uma assinatura existente, chamamos nosso endpoint /notification/push/public_key para retornar nossa publicKey, e com o valor dessa chave realizamos o subscribe, assim da próxima vez que tentarmos acessar a aplicação já vamos ter nossa inscrição feita.
Fiz também para fim de exemplo a chamada para o endpoint de registro da subscription e de envio da notificação, mas cabe a você definir quando e onde realizar o envio dessas notificações da melhor maneira.
async function handleServiceWorker() {
navigator.serviceWorker
.register('service-worker.js')
.then(async (serviceWorker) => {
serviceWorker.update();
let subscription = await serviceWorker.pushManager.getSubscription();
if (!subscription) {
const publicKeyResponse = await api.get<{ publicKey: string }>(
'/notification/push/public_key',
);
await serviceWorker.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: publicKeyResponse.data.publicKey,
});
subscription = await serviceWorker.pushManager.getSubscription();
}
await axios.post('http://localhost:3333/notification/push/register', {
subscription,
});
await axios.post('http://localhost:3333/notification/push/send', {
subscription,
});
});
}
obs: usei o
axioscomo lib para realizar a chamada HTTP, mas poderia ser ofetchnormalmente.
Convertendo Service Worker para JavaScript
Essa etapa não é necessária se você criou o arquivo service-worker.js, mas caso tenha criado ele em TypeScript execute os passos abaixo:
Instale a dependência esbuild como dependência de desenvolvimento:
$ npm i esbuild -D
Adicione o script abaixo no seu arquivo package.json:
"scripts": {
"build-sw": "esbuild public/service-worker.ts --outdir=public --bundle --sourcemap --minify --format=esm --legal-comments=external"
}
Agora é só executar o script build-sw:
$ npm run build-sw
Pronto, agora o service-worker.ts vai ser convertido para JavaScript.
Top comments (0)