Express tiene una idea arquitectónica real, y casi todo lo demás del framework se deriva de ella: una solicitud y una respuesta viajan a través de una cadena ordenada de pequeñas funciones, cada una de las cuales puede inspeccionarlas, modificarlas o finalizar el intercambio.
Ese es todo el modelo.
No hay un árbol de plugins, ni un contenedor de inyección de dependencias, ni un compilador de esquemas, solo un pipeline, y una apuesta deliberada de que la mayoría de los problemas del lado del servidor pueden descomponerse en pasos pequeños y componibles aplicados a un estado compartido.
Esta página es el modelo mental detrás de esa apuesta: de dónde vino el pipeline, cómo funciona realmente el despacho y qué dejó Express intencionalmente para que otros frameworks, y otras páginas de esta sección, tuvieran que completar.
Conceptos básicos de Express explica cómo construir rutas y middleware de forma práctica; Orden del middleware cubre las reglas prácticas de secuenciación que se derivan directamente del modelo descrito aquí.
Express despacha cada solicitud a través de una cadena ordenada de funciones (req, res, next), todas operando sobre los mismos objetos de solicitud y respuesta compartidos.
Por qué es importante: Casi todos los errores específicos de Express (un cuerpo faltante, una doble respuesta, un manejador de errores que nunca se activa) son en realidad un malentendido de esta única cadena, no una peculiaridad del framework.
Conceptos clave:middleware, la cadena de despacho, next(), Router, extensión de prototipo.
Cuándo usar este modelo: Para razonar sobre por qué el orden del middleware es importante, decidir dónde debe ir la lógica transversal y comprender qué aísla y qué no aísla un Router.
Limitaciones / Compromisos: El diseño de estado compartido y cadena lineal no tiene encapsulación incorporada y un techo de rendimiento real bajo tasas de solicitud muy altas, ambos resultados directos de priorizar la simplicidad sobre la estructura.
Temas relacionados: el modelo de solicitud/respuesta HTTP sin procesar, la encapsulación de plugins de Fastify, el pipeline impulsado por DI de NestJS, el middleware de manejo de errores.
Express se remonta directamente a Connect, una biblioteca de middleware anterior de Node construida sobre la misma idea: una lista de funciones, cada una con la oportunidad de actuar sobre una solicitud antes de pasar el control.
Express mantuvo ese núcleo y le agregó enrutamiento (app.get, app.post, parámetros de ruta), pero el modelo de ejecución subyacente, una cadena, no un árbol o un grafo, nunca cambió.
Una aplicación de Express es, en esencia, una función que se le entrega a http.createServer() como el listener de solicitudes; consulta El modelo de servidor HTTP de Node.js para ver lo que ese listener realmente recibe de Node.
Cuando llega una solicitud, Express toma el par IncomingMessage/ServerResponse sin procesar y los extiende con métodos y propiedades adicionales (req.params, res.json(), etc.) adjuntando el propio prototipo de Express; los objetos que ven tus manejadores son los mismos objetos de Node, solo que con más capacidades añadidas.
El middleware es simplemente cualquier función que coincida con la forma (req, res, next) => void: puede leer o mutar los req/res compartidos, y decide si la cadena continúa llamando a next() o termina enviando una respuesta.
function requestId(req: Request, _res: Response, next: NextFunction) { req.headers["x-request-id"] ??= crypto.randomUUID(); next(); // pasa el control a la siguiente función en la cadena}
Una analogía útil: imagina una única línea de ensamblaje, no una planta de fábrica ramificada; cada estación (middleware) estampa algo en la pieza que pasa o la retira completamente de la línea y la envía (envía la respuesta).
Internamente, app.use() y app.get()/app.post() no construyen un árbol de enrutamiento, sino que añaden capas a una lista ordenada, cada capa envolviendo un comparador de ruta/método más la función manejadora.
El despacho recorre esa lista desde arriba: para cada solicitud entrante, Express verifica si la ruta y el método de la siguiente capa coinciden, la ejecuta si es así y espera a que esa capa llame a next() antes de pasar a la siguiente.
Esta es exactamente la forma de cadena lineal que se muestra conceptualmente en Patrón de Middleware: el despachador real de Express es una versión más optimizada de esa misma idea, no un algoritmo diferente.
Un Router es una subcadena montable, no un ámbito aislado: app.use('/users', usersRouter) inserta la propia lista de capas internas del router en ese prefijo de ruta, pero cualquier extensión de prototipo, efecto secundario de middleware global o mutación de objeto compartido sigue aplicándose en todas partes; no hay un límite de encapsulación como lo hay en el modelo de plugins de Fastify.
next(err) es el único mecanismo de bifurcación de la cadena: llamar a next() con un argumento omite todas las capas normales restantes y salta directamente al middleware de manejo de errores de cuatro argumentos más cercano, razón por la cual los manejadores de errores deben registrarse al final; son el destino de respaldo para ese salto, no un hook especial que Express llama automáticamente.
Express 5 cambió una pieza importante de esta mecánica: las Promesas rechazadas devueltas por un manejador async ahora se reenvían automáticamente a esa misma ruta next(err), mientras que Express 4 las tragaba silenciosamente a menos que agregaras express-async-errors o envolvieras cada manejador manualmente.
La mayor fortaleza del modelo de pipeline es también su mayor limitación: debido a que cada middleware comparte los mismos req/res y se ejecuta estrictamente en el orden de registro, razonar sobre una aplicación Express grande es realmente razonar sobre una lista larga y plana; no hay un límite de módulo que te obligue a pensar en piezas más pequeñas como lo hacen el árbol de plugins de Fastify o el grafo de módulos de NestJS.
Esa planitud escala bien para la mayoría de las API CRUD, pero tiene un techo de rendimiento real: cada capa en una cadena larga agrega una llamada a función y una verificación de coincidencia de ruta a cada solicitud, incluso a aquellas que un middleware dado no le importan, lo cual es parte de la razón por la que el enfoque de enrutador compilado de Fastify supera a Express bajo tasas de solicitud muy altas; consulta Fastify vs Express ADR para ver el compromiso completo.
La seguridad en este modelo es completamente una cuestión de lo que hay en la cadena y en qué orden: Express no incluye manejo de CORS, limitación de velocidad ni encabezados de seguridad por defecto, confiando en el ecosistema (helmet, cors, express-rate-limit) para completarlos; consulta Middleware de seguridad.
La observabilidad sigue la misma filosofía: no hay registro de solicitudes o rastreo incorporado, solo una ranura app.use() cerca de la parte superior de la cadena donde un middleware de registro (o una instrumentación de OpenTelemetry) puede sentarse y ver pasar cada solicitud.
Abstracción central del framework
Fortaleza
Debilidad
Mejor ajuste
Express: cadena de middleware plana
Modelo mental simple; enorme ecosistema de middleware de fácil uso
Sin encapsulación; costo lineal por capa; fácil de desordenar
La mayoría de las API CRUD; equipos que valoran la amplitud del ecosistema sobre la estructura
Fastify: árbol de plugins encapsulado
Ámbitos aislados; despacho compilado por esquema para mayor velocidad
Más estructura inicial que aprender
API de alto rendimiento o basadas en esquemas
NestJS: grafo de módulos DI
Arquitectura forzada a escala; comprobable mediante DI
Abstracción más pesada; curva de aprendizaje más pronunciada
Equipos grandes, servicios empresariales de larga duración
"Un Router aísla su middleware como lo hace un plugin de Fastify." Solo delimita qué solicitudes llegan a un conjunto dado de capas por prefijo de ruta; comparte las mismas extensiones de prototipo globales y cualquier estado compartido mutado con el resto de la aplicación.
"El orden del middleware es una preferencia de estilo." Es un requisito de corrección: un analizador de cuerpo registrado después de una ruta que lee req.body verá undefined, porque la cadena ya ha pasado ese punto cuando el analizador se ejecutaría.
"Llamar a next() salta a la siguiente ruta coincidente, no al siguiente middleware." Avanza a la siguiente capa en el orden de registro, coincidiendo independientemente para cada capa; no tiene conocimiento de "rutas" como un concepto distinto del middleware.
"El middleware de manejo de errores se ejecuta automáticamente cuando un manejador lanza una excepción." Solo next(err) (o, en Express 5, una Promesa rechazada reenviada automáticamente) enruta al middleware de errores; un lanzamiento síncrono dentro de patrones no asíncronos más antiguos puede bloquear el proceso si no se captura.
"Express es un framework completo como Rails o Django." Es deliberadamente minimalista: solo enrutamiento y despacho; la validación, la integración ORM y la estructura se dejan al ecosistema o a algo construido encima, como NestJS.
¿Cuál es la abstracción central de Express, en una frase?
Una cadena ordenada de funciones (req, res, next), todas compartiendo los mismos objetos de solicitud y respuesta, recorridas en orden de registro para cada solicitud entrante.
¿De dónde surgió la idea de la cadena de middleware?
Express está construido directamente sobre Connect, una biblioteca de Node anterior que introdujo el mismo concepto de cadena de middleware lineal; Express añadió el enrutamiento sin cambiar ese modelo de ejecución subyacente.
¿Cómo despacha Express una solicitud internamente?
Recorre una lista interna ordenada de "capas" (cada una un comparador de ruta/método más un manejador), ejecutando cada una que coincide hasta que una capa finaliza la respuesta o la lista se agota; no es una búsqueda en árbol, es un escaneo lineal.
¿Un `Router` crea un ámbito aislado como un plugin de Fastify?
No, solo delimita qué solicitudes llegan a sus capas, por prefijo de ruta. Las extensiones de prototipo y cualquier estado mutable compartido siguen aplicándose en toda la aplicación, a diferencia de los contextos de plugin genuinamente encapsulados de Fastify.
¿Por qué el orden del middleware es tan importante en Express?
Porque la cadena es estrictamente lineal y con estado: un middleware posterior en la cadena solo puede ver los efectos (como un cuerpo analizado o un ID de usuario adjunto) del middleware que se ejecutó antes que él, nunca después.
¿Cómo cambia `next(err)` el despacho?
Omite todas las capas de firma normal restantes y salta directamente al middleware de manejo de errores de cuatro argumentos más cercano, razón por la cual esos manejadores deben registrarse al final, como destino de respaldo para ese salto.
¿Qué cambió en el manejo de errores en Express 5?
Las Promesas rechazadas devueltas por los manejadores de ruta async ahora se reenvían a next(err) automáticamente. En Express 4, una Promesa rechazada sin manejar dentro de un manejador asíncrono se tragaba silenciosamente a menos que agregaras herramientas adicionales o envolvieras cada manejador manualmente.
¿Por qué Express tiene un rendimiento inferior a Fastify con tasas de solicitud muy altas?
Cada capa en la cadena agrega una llamada a función y una verificación de coincidencia por solicitud, incluso para las solicitudes que esa capa finalmente ignora, un costo proporcional a la longitud de la cadena. Fastify compila su enrutamiento y serialización con anticipación, evitando gran parte de esa sobrecarga por solicitud.
¿Cuándo no debería usar el modelo de middleware de Express?
Cuando necesites un aislamiento genuino entre módulos de características (favorece la encapsulación de plugins de Fastify) o una arquitectura forzada y comprobable a gran escala de equipo (favorece el grafo de módulos DI de NestJS); la cadena plana y de estado compartido de Express va en contra de ambos objetivos por diseño.
¿Express tiene opiniones sobre la estructura del proyecto?
No, deliberadamente no. Proporciona solo despacho y enrutamiento; el análisis del cuerpo más allá de los incorporados, la validación, el acceso a la base de datos y la disposición de los archivos se dejan al desarrollador o a los paquetes del ecosistema.
¿Por qué la gente dice que Express es "solo Connect con enrutamiento"?
Porque eso es casi literalmente cierto: Express reutiliza el modelo de ejecución de la cadena de middleware de Connect y agrega la coincidencia de app.get/app.post/parámetros de ruta, en lugar de inventar un mecanismo de despacho diferente.
¿Este modelo hace que Express no sea adecuado para aplicaciones grandes?
No inherentemente; muchas API de producción grandes se ejecutan en Express con éxito, pero la falta de una estructura impuesta significa que el equipo tiene que imponer sus propias convenciones (routers por dominio, capas de servicio) en lugar de obtenerlas del framework.