Server-Sent Events em TypeScript: streaming de dados sem dor de cabeça
\n\nImagine: seu servidor gera dados — logs, notificações, atualizações de câmbio — e precisa enviá-los instantaneamente para o frontend. WebSocket? Poderoso, mas excessivo. Polling? Lento e consome tráfego. Existe um terceiro caminho — elegante, simples e incrivelmente útil: Server-Sent Events (SSE).
\n\nNeste artigo, vamos abordar SSE em TypeScript do início ao fim. Escreveremos cliente e servidor, aprenderemos a tratar erros e reconectar, e principalmente — entenderemos quando SSE realmente vence em relação a outras abordagens. Vamos lá!
\n\nO que são Server-Sent Events e para que servem?
\n\nServer-Sent Events é um padrão HTML5 que permite ao servidor enviar dados ao cliente por uma única conexão HTTP em tempo real. Diferente do WebSocket, SSE é unidirecional: apenas o servidor envia mensagens. Mas é justamente aí que está sua força — é extremamente simples e não requer bibliotecas adicionais.
\n\nQuando escolher SSE?
\n\n- \n
- Streaming unidirecional: notificações, logs, cotações de moedas, tickers de ações. \n
- Integração simples: funciona através do
EventSourcepadrão no navegador — sem pacotes npm. \n - Reconexão automática: o navegador restaura a conexão sozinho em caso de queda. \n
- Compatibilidade com HTTP/2: o multiplex não atrapalha, ao contrário do WebSocket. \n
Mas lembre-se: SSE não consegue enviar dados do cliente para o servidor (para isso, use requisições POST comuns) e não é suportado em navegadores antigos (IE — olá).
\n\nParte do servidor: escrevendo SSE em TypeScript + Express
\n\nVamos começar pelo backend. Criaremos um servidor simples em Node.js com Express e TypeScript. Instale as dependências:
\n\nnpm init -y\nnpm install express cors\nnpm install -D typescript @types/express @types/node ts-node\nnpx tsc --init\n\nAgora vamos escrever o servidor. O ponto chave é definir os cabeçalhos corretos e o formato das mensagens.
\n\n// server.ts\nimport express, { Request, Response } from 'express';\nimport cors from 'cors';\n\nconst app = express();\napp.use(cors());\n\n// Endpoint para SSE\napp.get('/events', (req: Request, res: Response) => {\n // Definindo cabeçalhos para SSE\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n\n // Enviando mensagem inicial\n res.write('data: {"message": "Conexão estabelecida"}\\n\\n');\n\n // Enviando evento a cada 3 segundos\n const intervalId = setInterval(() => {\n const data = JSON.stringify({\n time: new Date().toISOString(),\n value: Math.random() * 100,\n });\n res.write(`data: ${data}\\n\\n`);\n }, 3000);\n\n // Tratando desconexão do cliente\n req.on('close', () => {\n clearInterval(intervalId);\n res.end();\n console.log('Cliente desconectado');\n });\n});\n\nconst PORT = 3000;\napp.listen(PORT, () => console.log(`Servidor SSE rodando na porta ${PORT}`));\n\nObserve o formato: cada mensagem começa com data: e termina com duas quebras de linha \\n\\n. Também é possível especificar id, event e retry — mas isso é o básico.
Enviando eventos nomeados
\n\nÀs vezes é necessário diferenciar tipos de mensagens. Vamos adicionar o campo event:
res.write('event: update\\n');\nres.write(`data: ${JSON.stringify({ id: 1, title: 'Novo post' })}\\n\\n`);\n\n// E depois — outro evento\nres.write('event: notification\\n');\nres.write('data: {"level": "info", "text": "Servidor funcionando"}\\n\\n');\n\nNo cliente, poderemos nos inscrever em eventos específicos.
\n\nParte do cliente: recebendo eventos em TypeScript
\n\nAgora vamos escrever o cliente. No navegador, usa-se o EventSource nativo. Mas se