DEV Community

Cover image for Enviando Push Notification com Node.js para o Browser
Mayderson Mello
Mayderson Mello

Posted on • Edited on

Enviando Push Notification com Node.js para o Browser

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 
Enter fullscreen mode Exit fullscreen mode

Adicione no seu package.json o script de dev:

"scripts": { "dev": "tsx watch src/server.ts" } 
Enter fullscreen mode Exit fullscreen mode

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')); 
Enter fullscreen mode Exit fullscreen mode

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 }), ); }); 
Enter fullscreen mode Exit fullscreen mode

O código acima é bem simples, basicamente adicionamos um listener chamado push, com isso, quando for enviado alguma notificação vamos ficar sabendo, e teremos acesso ao evento que foi disparado, com esse evento conseguimos obter o payload 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(); 
Enter fullscreen mode Exit fullscreen mode

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ção handleServiceWorker 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, }); }); } 
Enter fullscreen mode Exit fullscreen mode

obs: usei o axios como lib para realizar a chamada HTTP, mas poderia ser o fetch 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 
Enter fullscreen mode Exit fullscreen mode

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" } 
Enter fullscreen mode Exit fullscreen mode

Agora é só executar o script build-sw:

$ npm run build-sw 
Enter fullscreen mode Exit fullscreen mode

Pronto, agora o service-worker.ts vai ser convertido para JavaScript.

Top comments (0)