Executando verificação de segurança...
8

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

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 JavasScript 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 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();

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,
      });
    });
}

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

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.

Carregando publicação patrocinada...
2

Cara, eu definitivamente vou salvar isso para utilizar em meus projetos. Muito obrigado pela disponibilidade. Este é um assunto de ouro e que é difícil de encontrar informações a respeito por aí. :)

2
1
1

Hahaha agradeço de mais o comentário, que bom que tenha gostado, é um assunto muito interessante mesmo que me propus a disponibilizar um tempo e escrever esse tutorial.

1
1

Alguém pode me ajuda com essa questão? Não manjo de Node, programo malemá em PHP, queria usar esse script em conjunto com meu php, a unica forma que encontrei. Alguém pode dar uma luz? Vou deixar meu whats ai 11984378224

1

Podes me tirar uma duvida?
estou precisando que minha api envie uma notificação para um usuario do app caso ele tenha um like.
utilizando esse tutorial que voce nos deu, ao criar o perfil, ele se registra com aquela rota da api, e quando ele receber um like, a api manda a mensagem? ou o app precisa requisitar a rota /notification/push/send pra poder receber?

1

Esse app é app mobile? se for e tiver usando um React Native ou Flutter da vida, recomendo fortemente usar o Firebase Push Notification, vai ser sua melhor opção.

Caso for um app na web mesmo, o que vai acontecer é que quando o usuário acessar sua aplicação, e aceitar as notificações o Service Worker será "instalado" no navegador, ai a rota /notification/push/register seria disparada, e você teria que implementar nessa rota uma forma de salvar a subscription enviada, e também o iddo usuário logado na sua aplicação, com isso, lá no seu Back-end você teria que implementar o envio da notificação nessa parte onde o usuário daria um like, ai enviaria o id de quem ele está curtindo, com isso na sua rota de envio de notificação, você iria consultar no banco o id desse usuário e traria a subscription dele, pois você já teria ela registrado, bom esse é o fluxo, espero que tenha entendido.