DEV Community

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

Posted on • Updated 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)