Quando algo mudar no servidor, como notificar o client/frontend? Embarque comigo nessa aventura pelo universo do Socket.io, onde aprenderemos como criar um server simples para push notifications de nossos APPs e clients.
O código-fonte deste teste está neste repositório do github
Analisando a demanda:
Necessidade: Notificar o client em tempo real quando algo específico acontecer.
Requisitos: Não depender de timeout(), escalável (não pode derrubar o server se houverem muitos usuários), Funcionar em conjunto, porém de forma independente com a API REST do serviço.
Ok, primeiro algumas observações: Uma técnica muito utilizada é o ‘Long Poling’, que consiste em solicitar uma página ao servidor, que só é enviada quando houver uma notificação. Ao receber a página - que só foi enviada pq houve notificação - imediatamente o client começa uma nova requisição. Vê o problema aí? A conexão fica aberta e ativa, e reinicia-se quando chega uma novidade. Aff. (é mano, não reclama tanto assim não, pq muita coisa boa já usou isso. Chamava-se commet (wikipedia, em inglês))
A Solução escolhida
Apesar dos HTML5 Server Send Events (SSE) parecerem super fofos e promissores, vamos de Websockets, que permitem um front-end independente (sem precisar estar no mesmo servidor ou domínio).
Pra facilitar ainda mais, já que estamos usando NodeJs, vamos de Socket.io.
Mãos a obra! - Setup básico
Primeiro, precisamos de uma estrutura básica em Node.JS. Só um server e algumas rotas. Vou usar o Express pra facilitar, então pode criar uma pasta e jogar os seguintes arquivos dentro:
(se não tiver o nodejs, baixe aqui)
1 | { |
Já pode ir acessando a pasta que vc criou via terminal e rodar lá dentro:1
$ npm install
1 | var express = require('express') |
Note a variável server
, onde carregamos o http, criamos o server e setamos a porta para 4555!
importante lembrar que a porta (4555) é apenas para o server http do socket.io. Ela está referenciada na variável server,que entra como parâmetro do listen()
do socket.io
A porta da nossa API (que usa express) é definida mais abaixo na var port
e usada quando chamados o app.listen(port)
.
Portanto, temos 2 servidores independentes: um na porta 4555 e outro na porta 8080, sendo o primeiro apenas para o socket.io
Agora rode o server
1 | $ node server |
E acesse http://localhost:8080/api/notificar veja se recebeu o retorno:
1 | { |
Criando o middleware para emitir o evento
Pra emitir o evento, criaremos um Middleware. Se você não sabe o que é um, pode querer ler O que é um middleware no ExpressJs e pra que serve? (AKA: Como interceptar requisições).
Logo abaixo do comentário /* Socket irá aqui depois */
, vamos inserir nosso middleware:
1 | (...) |
Neste código:
Criamos um Middleware que confere se o parâmetro notificacao veio preenchido (req.query.notificacao). Senão, define a var notificar
como string vazia.
Aí vemos:
se notificar
NÃO for uma string vazia, usamos io.emit
para enviar ela (variável notificar). Isso será feito disparando um evento que demos o nome de notificacao
.E daí chamamos o next()
.
Senão chamamos o next()
sem fazer nada.
Injetando o Middleware em nossa rota
Basta inserir a variável na qual declaramos o middleware como primeiro parâmetro do nosso verbo get()
, ou ativá-lo com o app.use()
. Qual você prefere? (To brincando, quem manda aqui sou eu ;P)
1 | (...) |
Pimba! Agora em qualquer rota nosso Middleware vai conferir se tem algo pra notificar, e o fará.
Recebendo as notificações.
Legal, agora precisamos receber essas notificações de alguma forma. A grande vibe dessa história é que não precisamos estar puxando arquivos do servidor para que funcione, então criaremos um arquivo html que vai representar o client.
O cliente poderia ser um app Web, ou um App Desktop, ou mesmo Mobile, enfim… Não importa. Crie o arquivo recebedor.html
conforme abaixo:
1 |
|
Aí é importante notarmos:
Carregamos socket.io.js pela url:
nossoservidor:porta/socket.io/socket.io.js
.
É possível carregar um arquivo direto, ou até por CDN, mas dessa forma garantimos que é a mesma versão utilizada no servidor, bem como que está funcionando a conexão.var socket = io('http://localhost:4555', {transports: ['websocket', 'polling', 'flashsocket']});
O primeiro parâmetro é a URL do servidor + porta em que o socket.io está emitindo. O segundo, uma config que define a prioridade de protocolos utilizados, sendowebsocket
primeiro.
Isso evita erros terrível de CORS com socket.iosocket.on('notificacao', function (data) {}
adiciona um eventListener para o eventonotificacao
que definimos lá no server.js comio.emit('notificacao', notificar);
.
Ao receber este evento, ele executa ofunction(data){}
, sendo data o conteúdo recebido.
Testando tudo
Execute o servidor
Abra o arquivo recebedor.html
Abra outra janela e acesse: http://localhost:8080/api/notificar?notificacao="isso é uma notificação em tempo real!”
volte para a janela do recebedor.html e veja como foi atualizado o conteúdo :)
Caso queira, o código-fonte deste teste está neste repositório do github
Conclusão
O socket.io é uma coisinha linda do papai pra criar interações em tempo real. Agora imagine que podemos instalar um server NodeJs em uma placa de circuito, tipo a Arduíno, e criar interações em tempo real entre aparelhos diversos e terminais?
No nosso exemplo aqui, falamos apenas de Push Notifications. Essa lógica será usada para notificar o admin de um sistema quando houver novos pedidos, e notificar o cliente quando houver alguma novidade no pedido dele. :)
Éi, mas vem cá… Conta pra mim, o que você já aprontou com Socket.io?
Comments