Middleware en Node.js: orden, API key y errores
En pocas palabras: En Express, un middleware es una función que recibe req, res y next y corre en el orden en que la registrás. Así centralizás logging, API key, validación y errores: la autenticación va antes de la ruta protegida y el manejador de errores, de cuatro parámetros, al final.
El middleware en Node.js es una función de Express que recibe req, res y next y corre en el orden en que la registrás. Una guía de dev.to del 10 de octubre de 2026 lo muestra con una API que encadena logging, API key, validación y manejo central de errores.
Un middleware en Express es una función con acceso al objeto de la request (req), al de la response (res) y a la siguiente función del ciclo, que por convención se llama next. Sirve para ejecutar código, modificar req y res, cerrar el ciclo o pasar el control. Según la documentación oficial de Express, una aplicación Express es, en el fondo, una serie de llamadas a middleware durante el ciclo request-response.
En este artículo:
- En 30 segundos
- ¿Qué es un middleware en Node.js y en qué orden se ejecuta en Express?
- ¿Qué diferencia hay entre middleware de aplicación, de ruta y de manejo de errores?
- ¿Cómo proteger una ruta con API key y validar el body con middleware?
- ¿Cómo manejar errores de forma centralizada en Express?
- ¿Qué le falta a este ejemplo para usarlo en producción?
- Errores comunes con el middleware en Express
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Express ejecuta los middlewares en el orden en que los registrás: la autenticación va antes de la ruta protegida y el manejador de errores va al final.
- Si un middleware no responde ni llama a next(), la request queda colgada.
- El manejador de errores central tiene cuatro parámetros (err, req, res, next) y, en la guía, devuelve el mensaje genérico “Internal server error.” cuando el status es 500 o más.
- Para producción, la guía recomienda logging estructurado, validación por esquemas, rate limiting y tests, y avisa que una API key de demostración no es un sistema de autenticación completo.
¿Qué es un middleware en Node.js y en qué orden se ejecuta en Express?
Express ejecuta los middlewares en el orden exacto en que los registrás con app.use() o app.METHOD(). Cada uno puede cortar el ciclo respondiendo, o llamar a next() para pasar el control al siguiente. Si no hace ninguna de las dos cosas, la request queda colgada, según la documentación oficial.
Ponele que registrás una ruta que responde y no llama a next(), y después un logger. Esa request nunca llega al logger (ejemplo hipotético, pero se desprende directamente de la regla anterior).
La guía de dev.to arma este pipeline, en este orden:
- express.json(): parsea el body JSON antes de que corra cualquier handler.
- requestLogger: loguea método y URL, y mide la duración cuando se dispara el evento finish de la respuesta.
- requestContext: crea req.context con un requestId y lo devuelve en el header X-Request-ID.
- Rutas: GET /health (pública) y POST /users (protegida).
- Handler de 404: convierte las rutas desconocidas en un error con status 404.
- Error handler: el último de la cadena.
Subís la app, probás /health, anda perfecto, probás POST /users sin la key y te devuelve un 401 prolijo, y recién ahí te das cuenta de que todo ese orden no es casualidad, es el diseño entero. Esto se conecta con lo que analizamos en implementar circuit breakers y timeouts en Node.js.
¿Qué diferencia hay entre middleware de aplicación, de ruta y de manejo de errores?

Express distingue cinco tipos de middleware: de aplicación, de router, de manejo de errores, built-in y de terceros, según su guía oficial. Lo que cambia es dónde se montan y cuántos parámetros reciben, no la idea de fondo.
- De aplicación: se asocia a la instancia de app con app.use() o app.METHOD(), por ejemplo app.get o app.post.
- De router: funciona igual, pero se asocia a una instancia de express.Router().
- De manejo de errores: lleva cuatro argumentos, (err, req, res, next).
- Built-in y de terceros: el express.json() del ejemplo es built-in.
Dos atajos que conviene conocer: next(‘route’) saltea el resto de la pila de esa ruta y pasa a la siguiente, y next(‘router’) sale del router completo. El primero solo funciona en middlewares cargados con app.METHOD() o router.METHOD().
¿Cómo proteger una ruta con API key y validar el body con middleware?
Se encadenan los middlewares en la definición de la ruta: primero requireApiKey, después validateUser y al final el handler. Así la lógica de negocio solo corre si la key es válida y el body pasó la validación. En la guía, /health queda público porque no los incluye.
function requireApiKey(req, res, next) {
const apiKey = req.get('x-api-key');
if (!apiKey || apiKey !== process.env.API_KEY) {
const error = new Error('A valid API key is required.');
error.status = 401;
return next(error);
}
next();
}
app.post('/users', requireApiKey, validateUser, (req, res, next) => {
/* crea el usuario con req.validatedUser */
});validateUser chequea que name sea un string no vacío y que email pase una regex, y deja el resultado limpio (trim y minúsculas) en req.validatedUser. La propia guía avisa que esa regex es ilustrativa y que en producción conviene una librería de validación.
Mi lectura del código (no lo ejecutamos): como requireApiKey va primero, un request sin key y con body inválido recibe 401, no 400. Está bien, porque no querés darle feedback de validación a quien no se autenticó. Eso sí, el !== no es una comparación de tiempo constante. En una API real yo evaluaría crypto.timingSafeEqual o, directamente, un esquema de credenciales por cliente. Lo explicamos a fondo en automatizar el despliegue con GitHub Actions.
Otro detalle útil de la guía: el servidor no arranca si falta la variable API_KEY, y deja process.exitCode en 1.
¿Cómo manejar errores de forma centralizada en Express?
Cada middleware crea un Error con una propiedad status (401, 400, 404) y lo pasa con next(error). Un único handler de cuatro parámetros, registrado después de todas las rutas, arma la respuesta. Así todas las fallas salen con el mismo formato.
app.use((err, req, res, next) => {
console.error('[Error handler] Request failed:', err.message);
if (res.headersSent) {
return next(err);
}
const status = Number.isInteger(err.status) ? err.status : 500;
const safeMessage = status >= 500 ? 'Internal server error.' : err.message;
res.status(status).json({
error: safeMessage,
requestId: req.context?.requestId || null,
});
});Tres decisiones del ejemplo valen la pena copiarlas. Si el status es 500 o más, el cliente ve un mensaje genérico y el detalle queda en el log. El requestId viaja en la respuesta de error, así que soporte puede cruzar un reclamo con el log. Y el chequeo de res.headersSent delega al handler por defecto, algo que la guía oficial de error handling pide cuando ya empezaste a escribir la respuesta, porque ahí el handler por defecto cierra la conexión.
Ahora bien, no todo error llega solo. Según esa misma guía, Express captura los errores síncronos sin trabajo extra, y los handlers async o las promesas devueltas llaman a next con el rechazo automáticamente. La doc que consultamos describe ese comportamiento; si tu proyecto usa otra versión de Express, confirmalo en la documentación de la tuya. Los callbacks tipo fs.readFile, en cambio, necesitan next(err) a mano, y en un setTimeout hace falta try/catch propio.
Si no escribís handler propio, el de Express devuelve el stack trace al cliente, salvo con NODE_ENV en production. Tema relacionado: elegir entre cron y systemd.
¿Qué le falta a este ejemplo para usarlo en producción?
Le faltan, por lo que dice la propia conclusión de la guía, logging estructurado, validación por esquemas, rate limiting, autenticación segura y tests automatizados del middleware. Una API key de demostración no alcanza como “autenticación” completa, y los detalles internos de los errores no deben llegar al cliente.
- Logging estructurado: los console.log numerados a sirven para entender el flujo, no para operar.
- Validación por esquemas: reemplaza la regex de email y el chequeo manual.
- Rate limiting: la guía lo menciona, pero el ejemplo no lo implementa.
- Tests: un test por middleware, con una sola responsabilidad cada uno.
Una inferencia mía, no de la fuente: como express.json() se registra antes que requestContext, un body JSON malformado fallaría antes de que exista req.context. Ese es probablemente el motivo del req.context?.requestId || null en el handler. Si lo vas a usar, probalo.
¿Cómo verificar que el pipeline se comporta como esperás?
Esta es una propuesta editorial, no un procedimiento de la fuente, y no la ejecutamos. Con el servidor levantado y API_KEY definida, estos cuatro pedidos deberían dar los resultados indicados:
curl -i http://localhost:3000/health
curl -i -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name":"Ana","email":"[email protected]"}'
curl -i -X POST http://localhost:3000/users -H "Content-Type: application/json" -H "x-api-key: $API_KEY" -d '{"name":"","email":"x"}'
curl -i http://localhost:3000/no-existe- Primero: 200, con el header X-Request-ID.
- Segundo (sin key): 401.
- Tercero (con key, name vacío): 400.
- Cuarto: 404 con el formato JSON del handler central.
Si algún status no coincide, revisá el orden de registro antes que la lógica de cada middleware. Te puede servir nuestra cobertura de comparativa de Bun y Node.js en Lambda.
Errores comunes con el middleware en Express
- Registrar el error handler antes de las rutas. Tiene que ir después de todos los app.use() y las rutas, como indica la doc de Express. Movelo al final.
- Escribir el handler con tres parámetros. Los de error llevan cuatro, (err, req, res, next). Con tres, Express lo trata como un middleware común. Aunque no uses next, dejalo en la firma.
- No llamar a next() ni responder. La request queda colgada hasta el timeout del cliente. Revisá todas las ramas, incluso los if que “no deberían pasar”.
- Escribir en la respuesta y después llamar a next(err) sin chequear headersSent. Agregá el return next(err) cuando headersSent sea true.
- Tragarse el error en callbacks y timers. En un fs.readFile o un setTimeout, el throw no llega a Express. Pasá el error con next(err) o envolvé en try/catch.
Preguntas Frecuentes
¿Qué es un middleware en Express y para qué sirve?
Es una función que recibe req, res y next, y sirve para ejecutar código, modificar la request o la response, cerrar el ciclo o pasar el control. Con ella centralizás autenticación, validación, logging y manejo de errores sin repetirlos en cada ruta.
¿Qué hace next() en Express?
next() pasa el control al siguiente middleware de la cadena. Si lo llamás con un argumento (salvo la cadena ‘route’), Express toma la request como un error y saltea los middlewares y rutas que no sean de manejo de errores.
¿Por qué importa el orden en que registro los middlewares en Express?
Importa porque Express los ejecuta en el orden de registro. La autenticación tiene que correr antes de la ruta protegida, y el manejador de errores va después de las rutas y del resto de los middlewares.
¿Cómo se hace un manejador de errores centralizado en Express?
Se define con app.use((err, req, res, next) => { … }) después de las rutas. Los demás middlewares le mandan los errores con next(error), y el handler arma la respuesta con el status y un mensaje seguro.
¿Cómo protejo una ruta con API key usando middleware?
Creás un middleware que lea el header x-api-key, lo compare con process.env.API_KEY y llame a next(error) con status 401 si no coincide. Después lo ponés antes del handler: app.post(‘/users’, requireApiKey, validateUser, handler).
Conclusión
La guía de dev.to no inventa nada nuevo sobre el middleware en Node.js, pero junta en un solo archivo las piezas que más se rompen: orden de registro, next(), error handler con cuatro parámetros y un 404 que pasa por el mismo camino que el resto de los errores. Si querés llevártelo a un proyecto, copiá la estructura y no el código tal cual. Cambiá la regex por un esquema, sacá la API key compartida y agregá rate limiting. Después corré los cuatro pedidos de arriba contra tu versión y fijate que los status coincidan.






