Conceptos básicos de HTTP en Node
10 ejemplos para entender HTTP en Node.js con http.createServer - 7 básicos y 3 intermedios.
Busca en todas las páginas de la documentación
10 ejemplos para entender HTTP en Node.js con http.createServer - 7 básicos y 3 intermedios.
Estos ejemplos asumen Node.js 24.18.0 (LTS activa) y TypeScript 5.6+ con ESM ("type": "module" en package.json).
mkdir http-basics-spike && cd http-basics-spike
npm init -y
npm pkg set type=module
npm install -D typescript@5.6 tsx @types/nodePara patrones a nivel de framework construidos sobre esta base, consulta Patrón de Middleware y Conceptos básicos de Express.
Todo servidor HTTP de Node comienza con http.createServer y un manejador de solicitudes.
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from Node HTTP\n");
});
server.listen(3000, () => {
console.log("Escuchando en http://localhost:3000");
});req es un IncomingMessage con method, url y headersres es un ServerResponse: debes llamar a res.end() o el cliente esperará indefinidamentewriteHead establece el estado y los encabezados en una sola llamada; úsalo antes de la primera writeRelacionado: Enrutamiento sin frameworks - coincide rutas sin Express
Enruta inspeccionando req.method y analizando req.url.
import { createServer } from "node:http";
import { parse } from "node:url";
const server = createServer((req, res) => {
const { pathname } = parse(req.url ?? "/", true);
if (req.method === "GET" && pathname === "/health") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "ok" }));
return;
}
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Not found" }));
});req.url incluye la cadena de consulta; usa URL o parse de node:url para la rutareq.path y req.queryLos encabezados llegan en minúsculas en Node 24. Accede a ellos a través de req.headers.
import { createServer } from "node:http";
const server = createServer((req, res) => {
const contentType = req.headers["content-type"] ?? "none";
const userAgent = req.headers["user-agent"] ?? "unknown";
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ contentType, userAgent }));
});, en un único valor de cadenaX-Forwarded-For sin configuración de proxy; consulta Conciencia de proxy inversoEstablece Content-Type: application/json y convierte tu carga útil a cadena.
import { createServer } from "node:http";
const users = [{ id: 1, name: "Ada" }];
const server = createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
});
res.end(JSON.stringify({ data: users }));
});charset=utf-8 para JSON con texto no ASCIIJSON.stringify con un reemplazador para fechas: date.toISOString()Recopila fragmentos de data del flujo de solicitud y luego analiza.
import { createServer } from "node:http";
async function readBody(req: import("node:http").IncomingMessage): Promise<string> {
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(chunk as Buffer);
}
return Buffer.concat(chunks).toString("utf8");
}
const server = createServer(async (req, res) => {
if (req.method !== "POST") {
res.writeHead(405).end();
return;
}
try {
const body = JSON.parse(await readBody(req));
res.writeHead(201, { "Content-Type": "application/json" });
res.end(JSON.stringify({ received: body }));
} catch {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Invalid JSON" }));
}
});express.json() de Express y el analizador integrado de Fastify manejan esto con límitesUsa códigos de estado significativos; los clientes y los balanceadores de carga dependen de ellos.
import { createServer } from "node:http";
const server = createServer((req, res) => {
if (req.method === "GET" && req.url === "/users/999") {
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "User not found" }));
return;
}
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ id: 1, name: "Ada" }));
});| Código | Significado | Cuándo usar |
|---|---|---|
| 200 | OK | GET, PUT, PATCH exitosos |
| 201 | Creado | POST exitoso que creó un recurso |
| 400 | Solicitud incorrecta | Fallo de validación, entrada mal formada |
| 404 | No encontrado | El recurso no existe |
| 500 | Error interno | Excepción no manejada (regístrala, no filtres detalles) |
Cierra el servidor en SIGTERM para que las solicitudes en curso finalicen.
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("ok\n");
});
server.listen(3000);
function shutdown() {
console.log("Apagando...");
server.close(() => {
console.log("Servidor cerrado");
process.exit(0);
});
setTimeout(() => process.exit(1), 10_000).unref();
}
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);server.close() deja de aceptar nuevas conexiones pero permite que las solicitudes activas se completenSIGTERM antes de eliminar los pods del servicioEvita que los clientes lentos mantengan las conexiones abiertas indefinidamente.
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("ok\n");
});
server.requestTimeout = 30_000; // 30s por solicitud
server.headersTimeout = 35_000; // debe exceder requestTimeout
server.keepAliveTimeout = 5_000; // tiempo de espera de socket keep-alive inactivo
server.listen(3000);requestTimeout (Node 18+) destruye las solicitudes que exceden el límiteheadersTimeout debe ser mayor que requestTimeoutkeepAliveTimeout para que coincida con tu proxy inverso; consulta Keep-Alive y límites de conexiónTransmite archivos grandes en lugar de leerlos en memoria.
import { createServer } from "node:http";
import { createReadStream } from "node:fs";
import { stat } from "node:fs/promises";
import { join } from "node:path";
const server = createServer(async (req, res) => {
const filePath = join(process.cwd(), "public", "report.pdf");
try {
const info = await stat(filePath);
res.writeHead(200, {
"Content-Type": "application/pdf",
"Content-Length": info.size,
});
createReadStream(filePath).pipe(res);
} catch {
res.writeHead(404).end();
}
});pipe maneja la contrapresión automáticamenteContent-Length cuando se conoce para mejores indicadores de progreso del clienteerror en el flujo de lectura para evitar respuestas colgadasnode:httpsEnvuelve el mismo patrón de manejador en https.createServer con certificados TLS.
import { createServer as createHttpsServer } from "node:https";
import { readFileSync } from "node:fs";
const server = createHttpsServer(
{
key: readFileSync("certs/key.pem"),
cert: readFileSync("certs/cert.pem"),
},
(req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("HTTPS ok\n");
}
);
server.listen(3443);mkcert o los certificados de desarrollo de tu plataformaRaramente para APIs de aplicaciones. Usa Express, Fastify o NestJS para enrutamiento, análisis y middleware. Conoce el módulo sin procesar para depuración, sidecars de verificación de estado y para entender lo que los frameworks abstraen.
Probablemente olvidaste res.end() o lo llamaste dos veces. Cada solicitud necesita exactamente una respuesta terminal. Verifica los caminos del código que no terminan sin finalizar.
Sí para E/S, pero las promesas rechazadas no manejadas en los manejadores http sin procesar no se capturan automáticamente. Envuelve en try/catch o usa un framework que maneje errores asíncronos (Express 5 lo hace).
writeHead envía el estado y los encabezados inmediatamente. setHeader pone en cola los encabezados hasta la primera write o end. Mezclarlos después de que comienzan las escrituras lanza ERR_HTTP_HEADERS_SENT.
Establece Access-Control-Allow-Origin y maneja la pre-solicitud OPTIONS manualmente, o usa un middleware de framework. Consulta Middleware de seguridad.
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.
Revisado por Chris St. John·Última actualización: 19 jul 2026