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
publicKey
para o Front-end./notification/push/register: Essa rota serviria para você salvar no Banco de Dados por exemplo a
subscription
que 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
subscription
no 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
.ts
pois TypeScript é vida ❤️, mas se você quiser pode usar.js
e 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
listener
chamadopush
, com isso, quando for enviado alguma notificação vamos ficar sabendo, e teremos acesso ao evento que foi disparado, com esse evento conseguimos obter opayload
que é 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çãohandleServiceWorker
que 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
axios
como lib para realizar a chamada HTTP, mas poderia ser ofetch
normalmente.
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)