¿Qué es un middleware en Express y cómo funciona dentro del ciclo de una petición?
Ver respuesta — intenta responderla en voz alta primero
Un middleware es una función que recibe tres argumentos: (req, res, next). Se ejecuta dentro del pipeline (la cadena de funciones) que Express corre por cada petición. Un middleware puede leer y modificar el objeto de la petición (req) y el de la respuesta (res), terminar la respuesta (por ejemplo con res.send(...)), o llamar a next() para pasar el control al siguiente middleware. Se registra con app.use(...).
Cuando llega una petición HTTP a tu servidor de Express, esa petición no salta directo a tu ruta final. Pasa por una fila de funciones, una tras otra, como una banda transportadora. Cada una de esas funciones es un middleware.
Cada middleware recibe siempre tres cosas:
req: el objeto de la petición (request). Aquí vive todo lo que mandó el cliente: la URL, los headers, el body, los query params. Puedes leerlo y también agregarle información (por ejemploreq.usuario = ...).res: el objeto de la respuesta (response). Con él le contestas al cliente (res.send,res.json,res.status).next: una función que, al llamarla, le dice a Express: "ya terminé mi parte, pásale la petición al siguiente de la fila".
Con esas herramientas, un middleware tiene solo dos caminos válidos:
- Terminar la respuesta él mismo (llamando a
res.send,res.json,res.end, etc.). Ahí la petición se acaba y ya no pasa a nadie más. - Llamar a
next()para ceder el control al siguiente middleware o a la ruta final.
Lo importante: debe hacer una de las dos cosas. Si no termina la respuesta y tampoco llama a next(), la petición se queda colgada y el cliente espera para siempre.
Los middleware sirven para tareas que se repiten en muchas rutas y que no quieres copiar y pegar: registrar logs, autenticar usuarios, parsear el body (como hace express.json()), validar datos, medir tiempos, agregar headers de seguridad, etc.
Se registran con app.use(...). Si le pones una ruta (app.use("/api", ...)) solo corre para esa ruta; si no le pones ruta, corre para todas las peticiones.
const express = require("express");
const app = express();
// Un middleware de log simple.
// Se ejecuta en CADA petición porque no le pasamos una ruta.
function logger(req, res, next) {
// Leemos información de la petición desde req.
const inicio = Date.now();
console.log(`Entró: ${req.method} ${req.url}`);
// Cedemos el control al siguiente middleware o ruta.
// Si olvidamos este next(), la petición se queda colgada.
next();
// (En un caso real, para medir el tiempo de respuesta
// se usaría res.on("finish", ...), no una línea después de next()).
console.log(`Salió: tardó ${Date.now() - inicio}ms`);
}
// Registramos el middleware. El ORDEN importa: debe ir ANTES de las rutas.
app.use(logger);
// Middleware que agrega datos a req para que las rutas los usen.
app.use((req, res, next) => {
req.horaDeLlegada = new Date().toISOString();
next();
});
// La ruta final: aquí SÍ terminamos la respuesta.
app.get("/", (req, res) => {
res.json({ mensaje: "Hola", llegada: req.horaDeLlegada });
});
app.listen(3000, () => {
console.log("Servidor en http://localhost:3000");
});
Olvidar llamar a next() (y tampoco responder) dentro del middleware. Cuando eso pasa, la petición nunca avanza: no llega a la ruta, no responde nada, y el cliente se queda esperando hasta que el navegador o el cliente cortan por timeout. La regla de oro es: cada middleware debe, o terminar la respuesta, o llamar a next(); nunca las dos, y nunca ninguna. Otro error relacionado es llamar a next() y además responder, lo que suele provocar el error Cannot set headers after they are sent to the client.
"Un middleware en Express es una función con la firma
(req, res, next)que se ejecuta dentro del pipeline de la petición. Recibe el objeto de la petición y el de la respuesta, y puede leerlos o modificarlos: por ejemplo, agregarreq.usuariotras autenticar. A partir de ahí tiene dos opciones válidas: terminar la respuesta con algo comores.json, o llamar anext()para pasarle el control al siguiente middleware o a la ruta. Si no hace ninguna de las dos, la petición se cuelga. Los registro conapp.use, y los uso para cosas transversales como logs, autenticación o parseo del body, así evito repetir la misma lógica en cada ruta."
¿Qué imprime este código cuando llega una petición a GET /, y por qué?
app.use((req, res, next) => {
console.log("A");
next();
console.log("B");
});
app.get("/", (req, res) => {
console.log("C");
res.send("ok");
});
Ver respuesta
Imprime A, luego C, luego B.
El middleware imprime "A" y llama a next(). Ese next() ejecuta de inmediato el siguiente eslabón del pipeline, que es la ruta GET /, la cual imprime "C" y responde. Cuando la ruta termina, el control regresa a la línea que seguía después de next() en el middleware, por eso "B" se imprime al final. La clave es entender que next() no "termina" el middleware: ejecuta lo que sigue y luego vuelve.