libro / blog / middleware-express

// middleware

Middleware en Express: qué es, cómo funciona y cómo escribir el tuyo

Gustavo Arias8 min de lectura

TL;DR Un middleware es una función (req, res, next) que se ejecuta entre la llegada de la petición y la respuesta. Puede leer o modificar la petición, responder, o pasar el control al siguiente con next(). El orden en que los registras es el orden en que se ejecutan.

Casi todo lo que hace Express, desde leer JSON hasta servir archivos o comprobar un login, está construido con middleware. Entender cómo funciona es entender Express. En el libro Node.js desde cero el middleware tiene un capítulo propio porque es la base de la autenticación, la validación y el manejo de errores que vienen después.

Qué es un middleware

Un middleware es una función con tres parámetros: (req, res, next). Express las encadena, y cada petición las atraviesa en orden. Cada una puede:

petición ─▶ express.json() ─▶ logger ─▶ autenticar ─▶ ruta ─▶ respuesta
                                             │
                                             └─▶ 401 si no hay token (la cadena se corta aquí)
Si un middleware no responde ni llama a next(), la petición queda colgada hasta que el cliente se canse de esperar.

El orden importa

Los middlewares se ejecutan en el orden en que se registran con app.use():

import express from 'express';

const app = express();

app.use(express.json());                 // 1. primero: deja req.body listo
app.use(express.static('public'));       // 2. archivos estáticos: si existe, responde y termina
app.use(logger);                         // 3. registra cada petición que llega hasta aquí
app.use('/api/admin', soloAdmin);        // 4. solo para las rutas que empiezan por /api/admin

app.get('/api/posts', listarPosts);      // 5. las rutas

app.use((req, res) => {                  // 6. si nada respondió: 404
  res.status(404).json({ error: 'Ruta no encontrada' });
});

Un error clásico es registrar express.json() después de las rutas: cuando la ruta se ejecuta, req.body todavía no existe.

Escribir tus propios middlewares

Un logger con tiempo de respuesta

export function logger(req, res, next) {
  const inicio = performance.now();

  // 'finish' se emite cuando la respuesta terminó de enviarse
  res.on('finish', () => {
    const ms = (performance.now() - inicio).toFixed(1);
    console.log(`${req.method} ${req.originalUrl} → ${res.statusCode} (${ms} ms)`);
  });

  next();
}

Un id único por petición

import { randomUUID } from 'node:crypto';

export function requestId(req, res, next) {
  req.id = randomUUID();
  res.set('X-Request-Id', req.id);   // útil para cruzar errores del cliente con los logs
  next();
}

Un middleware configurable

Si necesitás parámetros, escribí una función que devuelva el middleware:

export function requiereRol(...roles) {
  return (req, res, next) => {
    if (!req.usuario) {
      return res.status(401).json({ error: 'No autenticado' });
    }
    if (!roles.includes(req.usuario.rol)) {
      return res.status(403).json({ error: 'No tenés permiso para esta acción' });
    }
    next();
  };
}

// Uso: app.delete('/api/posts/:id', autenticar, requiereRol('admin', 'editor'), borrarPost);

El middleware autenticar, que verifica el token y rellena req.usuario, lo construimos en autenticación con JWT.

Middleware por ruta

Además de app.use() para todas las rutas, podés aplicar middlewares solo a una ruta o a un router, pasándolos antes del manejador final:

// Solo esta ruta exige estar autenticado y ser admin
app.get('/api/usuarios', autenticar, requiereRol('admin'), listarUsuarios);

// Todas las rutas de este router exigen autenticación
const privado = Router();
privado.use(autenticar);
privado.get('/perfil', verPerfil);
app.use('/api/me', privado);

Middlewares de terceros imprescindibles

PaqueteQué hace
helmetAñade cabeceras HTTP de seguridad (CSP, HSTS, etc.)
corsControla qué dominios pueden llamar a tu API desde el navegador
express-rate-limitLimita el número de peticiones por IP: frena fuerza bruta y abusos
compressionComprime las respuestas (si no lo hace ya tu proxy inverso)
import cors from 'cors';
import helmet from 'helmet';
import { rateLimit } from 'express-rate-limit';

app.use(helmet());
app.use(cors({ origin: ['https://mi-frontend.com'] }));   // nunca '*' si usás credenciales
app.use('/api/auth', rateLimit({ windowMs: 15 * 60 * 1000, limit: 20 }));   // 20 intentos / 15 min

Hay un tipo especial de middleware, con cuatro parámetros (err, req, res, next), que solo se ejecuta cuando hay un error. Lo vemos en validación y manejo de errores.

Referencia: uso de middleware en la documentación de Express.

La pieza central de Express

El libro Node.js desde cero dedica un capítulo al middleware y su aplicación, la base de la autenticación, la validación y el manejo de errores.

Comprar en Amazon