Server-Sent Events
Envie atualizações do servidor para o cliente por uma conexão HTTP persistente com Server-Sent Events (SSE).
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import express from "express";
const app = express();
app.get("/events", (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 1000);
req.on("close", () => clearInterval(interval));
});Quando usar isso: Feeds ao vivo, notificações, atualizações de progresso e streaming de logs onde apenas o servidor envia dados.
Exemplo de Trabalho
import express from "express";
const app = express();
const clients = new Set<import("node:http").ServerResponse>();
app.get("/events", (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"X-Accel-Buffering": "no", // desabilita o buffering do nginx
});
res.write(": connected\n\n"); // linha de comentário mantém a conexão viva
clients.add(res);
req.on("close", () => {
clients.delete(res);
});
});
function broadcast(event: string, data: unknown) {
const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
for (const client of clients) {
client.write(message);
}
}
// Disparar de outro lugar no aplicativo
app.post("/notify", express.json(), (req, res) => {
broadcast("notification", req.body);
res.json({ sent: clients.size });
});Cliente (navegador):
const source = new EventSource("/events");
source.addEventListener("notification", (e) => {
console.log(JSON.parse(e.data));
});
source.onerror = () => console.log("SSE reconectando...");O que isso demonstra:
- Cabeçalhos de resposta SSE, incluindo a desativação do buffering do nginx
- Eventos nomeados com o campo
event: - Conjunto de clientes para fanout de broadcast
- Limpeza na desconexão do cliente
EventSourcedo navegador com reconexão automática
Mergulho Profundo
Como Funciona
- SSE é uma resposta HTTP de longa duração com
Content-Type: text/event-stream - Mensagens são linhas de texto:
data: ...\n\n(duas novas linhas terminam a mensagem) - Campos opcionais:
event:,id:,retry: - O
EventSourcedo navegador reconecta automaticamente comLast-Event-ID
Formato da Mensagem SSE
event: price-update
id: 42
data: {"symbol": "AAPL", "price": 182.50}
Configuração do nginx
location /events {
proxy_pass http://node_backend;
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
}Armadilhas
- Buffering do nginx para SSE - os clientes veem eventos atrasados ou em lotes. Correção:
proxy_buffering offeX-Accel-Buffering: no. - Não limpar na desconexão - vazamento de memória de objetos de resposta obsoletos. Correção: remover do conjunto de clientes em
close. - Escrever em resposta fechada - lança
ERR_STREAM_DESTROYED. Correção: verificarres.writableEndedantes de escrever. - Sem autenticação no endpoint SSE - qualquer um pode se inscrever. Correção: validar cookie/token no GET inicial (EventSource suporta cookies).
- Transmitir para todos os clientes sem limite - picos de CPU. Correção: limitar a taxa de fanout; usar salas/canais.
- Usar SSE para bidirecional - o cliente não pode enviar via SSE. Correção: o cliente envia via HTTP POST normal; o servidor envia via SSE.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| WebSocket | Mensagens bidirecionais | Push apenas do servidor |
| Long-polling | Restrições de proxy muito antigas | Infraestrutura moderna |
| Socket.IO | Necessidade de salas e fallback | Push unidirecional simples |
| Webhook para cliente | Não aplicável (sem push do servidor) | Atualizações de UI em tempo real |
FAQs
SSE ou WebSocket para dashboards ao vivo?
SSE. Dashboards são do servidor para o cliente. SSE é mais simples, amigável a HTTP e reconecta automaticamente.
Quantos clientes SSE por processo Node?
Semelhante ao WebSocket: 10k-50k conexões ociosas. Broadcast ativo reduz a capacidade.
SSE funciona através do Cloudflare?
Sim, mas verifique os limites de tempo limite. O Cloudflare pode fechar conexões de longa duração após 100 segundos no plano gratuito.
Como autenticar SSE?
Autenticação baseada em cookie funciona com EventSource. Para autenticação de token, passe como parâmetro de consulta (menos seguro) ou use um polyfill de SSE baseado em fetch.
Posso usar SSE com Fastify?
Sim, via reply.raw para escrita direta de stream. Ou use o plugin @fastify/sse.
O que é o campo retry?
retry: 5000\n\n diz ao navegador para esperar 5 segundos antes de reconectar após a desconexão.
Como escalar SSE entre instâncias?
Pub/sub do Redis: cada instância se inscreve e escreve para seus clientes SSE locais. Veja Escalando Tempo Real.
O HTTP/2 afeta o SSE?
SSE funciona sobre HTTP/2. A multiplexação ajuda quando a mesma conexão atende a outras requisições.
Relacionado
- Noções Básicas de Tempo Real - SSE vs WebSocket
- Escalando Tempo Real - SSE multi-instância
- Considerações sobre HTTP/2 e HTTP/3 - configuração de proxy
- Keep-Alive e Limites de Conexão - ajuste de conexão
- Melhores Práticas de Tempo Real - checklist da seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.