Criando Servidores HTTP
Três maneiras de construir servidores HTTP em Node.js - node:http para zero dependências, Express para familiaridade, Fastify para performance.
Busque em todas as páginas da documentação
Três maneiras de construir servidores HTTP em Node.js - node:http para zero dependências, Express para familiaridade, Fastify para performance.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida - pronto para copiar e colar.
// 1. node:http - zero dependências
import { createServer } from "node:http";
createServer((req, res) => {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: true }));
}).listen(3000);
// 2. Express - a escolha familiar
import express from "express";
const app = express();
app.use(express.json());
app.get("/", (req, res) => res.json({ ok: true }));
app.listen(3000);
// 3. Fastify - alta performance
import Fastify from "fastify";
const fastify = Fastify({ logger: true });
fastify.get("/", async () => ({ ok: true }));
await fastify.listen({ port: 3000 });Quando usar isso: Sempre que precisar de um serviço HTTP de longa duração - APIs internas, webhooks, fallbacks de SSR, workers em segundo plano com um endpoint de health check.
Uma pequena API REST com três rotas - GET /, GET /users/:id, POST /users - lado a lado em todos os três frameworks.
// --- node:http ---
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
type User = { id: string; name: string };
const users = new Map<string, User>();
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
res.setHeader("content-type", "application/json");
if (req.method === "GET" && req.url === "/") {
res.end(JSON.stringify({ ok: true }));
return;
}
const idMatch = req.url?.match(/^\/users\/([^/]+)$/);
if (req.method === "GET" && idMatch) {
const user = users.get(idMatch[1]);
if (!user) {
res.statusCode = 404;
res.end(JSON.stringify({ error: "not found" }));
return;
}
res.end(JSON.stringify(user));
return;
}
if (req.method === "POST" && req.url === "/users") {
let body = "";
for await (const chunk of req) body += chunk;
const parsed = JSON.parse(body) as User;
users.set(parsed.id, parsed);
res.statusCode = 201;
res.end(JSON.stringify(parsed));
return;
}
res.statusCode = 404;
res.end(JSON.stringify({ error: "not found" }));
});
server.listen(3000, () => console.log("node:http on :3000"));// --- Express ---
import express, { type Request, type Response } from "express";
type User = { id: string; name: string };
const users = new Map<string, User>();
const app = express();
app.use(express.json());
app.get("/", (_req: Request, res: Response) => {
res.json({ ok: true });
});
app.get("/users/:id", (req: Request<{ id: string }>, res: Response) => {
const user = users.get(req.params.id);
if (!user) return res.status(404).json({ error: "not found" });
res.json(user);
});
app.post("/users", (req: Request<unknown, unknown, User>, res: Response) => {
users.set(req.body.id, req.body);
res.status(201).json(req.body);
});
app.listen(3000, () => console.log("express on :3000"));// --- Fastify ---
import Fastify from "fastify";
type User = { id: string; name: string };
const users = new Map<string, User>();
const fastify = Fastify({ logger: true });
fastify.get("/", async () => ({ ok: true }));
fastify.get<{ Params: { id: string } }>("/users/:id", async (req, reply) => {
const user = users.get(req.params.id);
if (!user) return reply.code(404).send({ error: "not found" });
return user;
});
fastify.post<{ Body: User }>(
"/users",
{
schema: {
body: {
type: "object",
required: ["id", "name"],
properties: {
id: { type: "string" },
name: { type: "string" },
},
},
},
},
async (req, reply) => {
users.set(req.body.id, req.body);
return reply.code(201).send(req.body);
}
);
await fastify.listen({ port: 3000 });O que isso demonstra:
node:http força você a lidar com roteamento, parsing de corpo e códigos de status por conta própria - mínimo, mas verboso.express.json()) fornece req.body gratuitamente e usa semânticas familiares de req/res.Todos os três frameworks, em última análise, encapsulam node:http. createServer retorna um http.Server - que é um EventEmitter - que emite 'request' para cada mensagem recebida. Express e Fastify instalam um único listener 'request' que executa seus pipelines de roteamento e middleware.
Express usa uma cadeia linear de middleware: cada função chama next() para passar o controle. Fastify usa um sistema de hooks baseado em esquema com serializadores JSON pré-compilados, que é de onde vem a maior parte de sua velocidade.
node:http com parsing JSON. Não há parser de corpo integrado - leia a requisição como um iterador assíncrono de buffers e analise manualmente (veja o exemplo de trabalho). Para corpos grandes, proteja-se contra memória ilimitada com um limite de bytes.
Cadeia de middleware Express. A ordem importa. Coloque express.json() antes das rotas que precisam de req.body, coloque os manipuladores de erro (funções com quatro parâmetros) por último, e monte CORS/helmet antes de qualquer lógica de negócios.
Esquemas Fastify. Declarar um esquema JSON para body, params, querystring e response valida a entrada automaticamente e compila um serializador rápido para a resposta. Você obtém erros 400 gratuitamente em entradas inválidas.
Handlers de rota assíncronos. Express 4 engole silenciosamente erros lançados em handlers assíncronos, a menos que você os envolva ou use express-async-errors. Express 5 encaminha promessas rejeitadas para o middleware de erro. Fastify aguarda handlers assíncronos nativamente.
Shutdown gracioso. Escute por SIGTERM, pare de aceitar novas conexões com server.close(), e drene requisições em andamento antes de sair:
process.on("SIGTERM", () => {
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 10_000).unref();
});Endpoint de health check. Adicione GET /healthz retornando 200 para liveness. Mantenha-o sem dependências para que um banco de dados quebrado não falhe a sonda de liveness - use /readyz para verificações de dependência.
Middleware CORS e de erro. Para Express, app.use(cors()) cedo, e um manipulador de erro final (err, req, res, next) => ... por último. Para Fastify, registre @fastify/cors e use setErrorHandler.
Para node:http, importe IncomingMessage e ServerResponse de node:http e anote a assinatura do handler. Para Express, instale @types/express e use Request e Response - Request aceita genéricos para params, corpo da resposta, corpo da requisição e query: Request<Params, ResBody, ReqBody, Query>. Para Fastify, passe um objeto genérico diretamente para a rota: fastify.get<\{ Params: \{ id: string \} \}>("/users/:id", handler) (escape os chaves ao escrever isso em markdown). Os genéricos do Fastify também suportam Body, Querystring, Headers e Reply.
res.end() em node:http. A requisição fica pendurada até que o cliente atinja o timeout. Cada caminho de código deve chamar res.end() (ou res.writeHead().end()), incluindo ramos de erro.express-async-errors, ou atualize para Express 5.express.json() após uma rota significa que req.body é undefined. CORS após as rotas significa que requisições preflight retornam 404. Manipuladores de erro antes das rotas nunca são acionados.OPTIONS antes de requisições não simples. Se seu roteador registra apenas GET/POST, o preflight falha. Use um middleware CORS que lide com OPTIONS globalmente.EADDRINUSE ao reiniciar. O processo anterior ainda está segurando a porta. Mate-o, espere pela janela TIME_WAIT, ou defina server.listen(\{ port, exclusive: false \}) - chaves escapadas em prosa.return e res.send no Express. Retornar um valor não faz nada; Express só responde quando você chama res.send/res.json. No Fastify, o oposto - retornar o valor o envia.| Opção | Melhor Para | Notas |
|---|---|---|
node:http | Serviços mínimos, aprendizado | Zero dependências, controle máximo, boilerplate máximo. |
| Express | Aplicações legadas, ecossistema enorme | Familiar, lento em relação ao Fastify, primeiro middleware. |
| Fastify | APIs JSON de alta vazão | Validação de esquema, serializador rápido, ótimo suporte a TS. |
| Hono | Runtimes de edge (Workers, Deno, Bun) | Pequeno, baseado em fetch, roda em Node também. |
| Koa | Sequência dos criadores do Express | Middleware async, núcleo menor, ecossistema menor. |
| h3 | Motor de servidor Nitro / Nuxt | Componível, funciona em Node e edge. |
| Rotas de API Next.js | Aplicações já no Next.js | Co-localizado com o frontend, menos flexível para trabalho não-HTTP. |
Bun.serve | Runtime Bun | Baseado em fetch, extremamente rápido, apenas Bun. |
Fastify é a opção segura para APIs JSON - rápido, validado por esquema, ótimo suporte a TypeScript. Escolha Express apenas se sua equipe já o conhece ou se você precisa de um middleware específico. Escolha node:http apenas para pequenas ferramentas internas onde uma dependência é um impeditivo.
Itere a requisição como um iterador assíncrono de buffers, concatene em uma string e então JSON.parse. Sempre imponha um tamanho máximo - rastreie os bytes lidos e rejeite se exceder seu limite, ou um cliente malicioso pode causar OOM no processo.
http.Server e EventEmitter?http.Server estende EventEmitter e emite 'request', 'connection', 'close' e 'error'. Express e Fastify instalam um único listener 'request' que executa seus próprios pipelines.
Escute por SIGTERM, chame server.close() para parar de aceitar novas conexões, deixe as requisições em andamento terminarem, e então saia. Adicione um timeout (ex: 10s) que força a saída se algo ficar pendurado, e chame .unref() no timer para que ele não mantenha o loop vivo por si só.
EADDRINUSE ao reiniciar meu servidor de desenvolvimento? (Armadilha)O processo anterior ainda está segurando a porta - ou ele não foi encerrado corretamente, ou o sistema operacional ainda está na janela TIME_WAIT. Mate o processo órfão (lsof -i :3000) ou altere a porta. Um manipulador de shutdown gracioso evita isso em reinícios intencionais.
No Express 4, erros lançados e promessas rejeitadas de handlers assíncronos são silenciosamente engolidos. Envolva handlers em try/catch, instale express-async-errors, ou atualize para Express 5 que encaminha rejeições para o middleware de erro automaticamente.
Request aceita quatro genéricos: Request<Params, ResBody, ReqBody, Query>. Para GET /users/:id, use Request<\{ id: string \}>. Para um POST com um corpo tipado, use Request<unknown, unknown, MyBody>. Instale @types/express primeiro.
Passe um objeto genérico para o método de rota: fastify.get<\{ Params: \{ id: string \}; Querystring: \{ q: string \} \}>(...). As chaves suportadas são Params, Querystring, Body, Headers e Reply. Fastify infere req.params, req.query e req.body a partir desses tipos.
Você pode definir Access-Control-Allow-Origin e similares manualmente, mas também deve lidar com requisições preflight OPTIONS. Uma biblioteca (cors para Express, @fastify/cors para Fastify) lida com ambos em uma linha e evita bugs sutis.
Express executa o middleware na ordem de registro. Middleware registrado após app.get(...) só é executado para rotas registradas depois. Sempre monte parsers, CORS e loggers antes de suas rotas.
Express só responde quando você chama res.send / res.json - retornar um valor não faz nada. Fastify é o oposto: retornar o valor o envia automaticamente. Misturar os dois modelos mentais é uma fonte comum de requisições penduradas.
Se sua aplicação já está no Next.js e o endpoint está intimamente acoplado ao frontend, sim - a co-localização é valiosa. Para serviços independentes, trabalhos de longa duração ou qualquer coisa que precise de comportamento de servidor personalizado (WebSockets, streaming, shutdown gracioso), um servidor dedicado Fastify ou Express é uma opção melhor.
docs/nodejs-scripts/event-emitter.md - http.Server é um EventEmitter, então os mesmos padrões se aplicam.docs/nextjs-routing/app-router.md - Handlers do App Router do Next.js como alternativa para endpoints acoplados à aplicação.docs/nextjs-data/fetching.md - chamando APIs HTTP do lado do cliente e do servidor de uma aplicação Next.js.Revisado por Chris St. John·Última atualização: 10 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥