Patrón Middleware
Construye pipelines de solicitudes a partir de funciones pequeñas y componibles, donde cada una maneja una preocupación antes de pasar el control al siguiente manejador.
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import express, { type Request, type Response, type NextFunction } from "express";
const app = express();
// El middleware se ejecuta en el orden de registro
app.use((req, _res, next) => {
req.headers["x-request-start"] = String(Date.now());
next();
});
app.get("/users", (req, res) => {
res.json({ startedAt: req.headers["x-request-start"] });
});
// Middleware de error de cuatro argumentos (Express 5)
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
res.status(500).json({ error: err.message });
});Cuándo usarlo: Cualquier aplicación HTTP que necesite que las preocupaciones transversales (registro, autenticación, análisis, CORS) se apliquen de manera consistente en todas las rutas.
Ejemplo de trabajo
Un ejecutor de middleware mínimo sin Express, luego la misma cadena en Express 5.
import express from "express";
// --- Tipos de middleware de Node puro ---
type Req = { headers: Record<string, string | undefined>; body?: unknown };
type Res = { statusCode: number; body?: unknown; status(n: number): Res; json(d: unknown): void };
type Next = (err?: Error) => void;
type Middleware = (req: Req, res: Res, next: Next) => void;
function runMiddleware(
middlewares: Middleware[],
req: Req,
res: Res,
finalHandler: () => void
) {
let index = 0;
const next: Next = (err) => {
if (err) throw err;
const fn = middlewares[index++];
if (fn) fn(req, res, next);
else finalHandler();
};
next();
}
// --- Equivalente a Express 5 ---
const app = express();
app.use(express.json({ limit: "1mb" }));
function requestId(req: express.Request, _res: express.Response, next: express.NextFunction) {
req.headers["x-request-id"] = crypto.randomUUID();
next();
}
function requireAuth(req: express.Request, res: express.Response, next: express.NextFunction) {
const token = req.headers.authorization;
if (!token?.startsWith("Bearer ")) {
res.status(401).json({ error: "Unauthorized" });
return;
}
next();
}
app.use(requestId);
app.get("/public", (_req, res) => res.json({ ok: true }));
app.get("/private", requireAuth, (_req, res) => res.json({ secret: true }));Lo que esto demuestra:
- El middleware es una función con la firma
(req, res, next) - Llama a
next()para continuar; omítelo para finalizar la respuesta - El orden importa: los analizadores antes de las rutas, los manejadores de errores al final
- El middleware a nivel de ruta (
requireAuth) limita la autenticación a rutas específicas
En detalle
Cómo funciona
- Cada middleware puede mutar
req/res, finalizar la respuesta o llamar anext() - La cadena es una invocación enlazada:
m1 -> m2 -> m3 -> manejador de ruta - El middleware de error tiene 4 parámetros
(err, req, res, next)y solo se ejecuta cuando se llama anext(err) - Fastify usa hooks (
onRequest,preHandler) en lugar de una sola cadena; NestJS usa guards, interceptors y pipes
Categorías de Middleware
| Categoría | Ejemplos | Posición típica |
|---|---|---|
| Análisis de solicitudes | express.json(), express.urlencoded() | Temprano |
| Seguridad | helmet, cors, limitador de velocidad | Después de los analizadores |
| Contexto | ID de solicitud, registro, temporización | Antes de las rutas |
| Autenticación | Verificación JWT, verificación de clave API | Por ruta o grupo de enrutadores |
| Negocio | Manejadores de ruta | Medio |
| Manejo de errores | Middleware de error de 4 argumentos | Último |
Notas de TypeScript
// Extiende Express Request para un contexto de middleware tipado
declare global {
namespace Express {
interface Request {
userId?: string;
}
}
}
function attachUser(req: express.Request, _res: express.Response, next: express.NextFunction) {
req.userId = "user-42";
next();
}Errores comunes
- Llamar a
next()después deres.send()- envía respuestas duplicadas y falla conERR_HTTP_HEADERS_SENT. Solución: regresa inmediatamente después de finalizar la respuesta. next()olvidado - la solicitud se cuelga hasta que se agota el tiempo de espera. Solución: cada ruta de código debe llamar anext()o finalizar la respuesta.- Middleware de error registrado antes de las rutas - nunca se ejecuta. Solución: registra los manejadores de 4 argumentos después de todas las rutas.
- Middleware asíncrono sin reenvío de errores - las promesas rechazadas se pierden en patrones más antiguos. Solución: Express 5 reenvía errores asíncronos; de lo contrario, usa
try/catchynext(err). - Autenticación global en archivos estáticos - bloquea
favicon.icoy las comprobaciones de estado. Solución: monta la autenticación en enrutadores específicos, noapp.use(auth)globalmente. - Orden de los analizadores de cuerpo -
express.json()después de las rutas significa que los cuerpos POST nunca se analizan. Solución: los analizadores primero. Consulta Orden de Middleware.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Hooks de Fastify | Necesitas etapas de ciclo de vida (preValidation, preSerialization) | El equipo solo conoce Express |
| Guards/pipes de NestJS | Transversalidad basada en decoradores con DI | API CRUD simple |
| Lógica de manejador en línea | Script de un solo endpoint | Más de 2 preocupaciones compartidas |
| Middleware de Hono | Edge + Node con un paquete pequeño | Se requiere un ecosistema de plugins de Express pesado |
Preguntas frecuentes
¿Cuál es la diferencia entre `app.use(fn)` y `app.get(path, fn)`?
app.use coincide con todos los métodos y rutas (a menos que se proporcione un prefijo de ruta). app.get solo coincide con las solicitudes GET a una ruta específica. El middleware de autenticación generalmente se coloca en app.use("/api", auth).
¿Cuántas funciones de middleware son demasiadas?
No hay un límite estricto, pero si la cadena excede los 10 middlewares globales, audita para detectar redundancias. Combina el registro + ID de solicitud en un solo middleware.
¿Puede el middleware ser asíncrono?
Sí. Express 5 captura automáticamente los errores asíncronos. En Node puro o Express 4, envuélvelo con una función auxiliar que llame a next(err) en caso de rechazo.
¿Cómo se relaciona esto con los plugins de Fastify?
Los plugins de Fastify encapsulan rutas + hooks + decoradores en un ámbito. El middleware es plano; los plugins evitan la fuga de decoradores entre módulos de características.
¿Debo usar middleware para la lógica de negocio?
No. El middleware maneja la infraestructura transversal. Las reglas de negocio pertenecen a las funciones de servicio llamadas por manejadores de ruta delgados.
¿Cómo pruebo el middleware de forma aislada?
Llama a la función con req, res simulados y un espía next. Afirma que se llamó a next o que se estableció res.status. Supertest prueba la cadena completa.
¿Qué pasa con el patrón `ctx` de Koa?
Koa usa middleware asíncrono con ctx en lugar de (req, res, next). Consulta Koa y Polka.
¿El middleware se ejecuta en solicitudes 404?
Solo si se registra antes del manejador 404. Agrega una ruta comodín o un middleware de error al final de la cadena.
Relacionado
- Orden de Middleware - secuencia de registro correcta
- Manejo de errores en Express - patrones de middleware de error
- Middleware de seguridad - helmet, cors, límites de velocidad
- Guards, Interceptors y Pipes - equivalente de NestJS
- Mejores prácticas de fundamentos HTTP - lista de verificación de la sección
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.