Creación y Uso de Middlewares en Express
En el desarrollo de APIs RESTful con Node.js y Express, los middlewares constituyen uno de los conceptos más poderosos y versátiles del framework. Permiten crear capas de procesamiento que se ejecutan de forma secuencial, facilitando la separación de preocupaciones y la reutilización de código.
¿Qué es un Middleware?
Un middleware es esencialmente una función que recibe tres parámetros: request, response y next. Cada middleware puede:
- Ejecutar cualquier código.
- Realizar cambios en los objetos request y response.
- Terminar el ciclo de solicitud-respuesta.
- Llamar al siguiente middleware en la pila.
Tipos de Middlewares en Express
| Tipo | Descripción | Ejemplo de uso |
|---|---|---|
| Integrado | Incluido con Express | express.json(), express.static() |
| De terceros | Instalados via npm | morgan, cors, helmet |
| Nivel de aplicación | Aplica a todas las rutas | Autenticación global |
| Nivel de router | Aplica a rutas específicas | Middleware por grupo de rutas |
| Nivel de manejo de errores | Con firma de 4 parámetros | Gestión centralizada de errores |
Creando tu Primer Middleware Personalizado
Veamos cómo crear un middleware desde cero. Empezaremos con un ejemplo sencillo que registre información de cada petición:
// middleware/logger.js
const loggerMiddleware = (req, res, next) => {
const timestamp = new Date().toISOString();
const method = req.method;
const url = req.originalUrl;
console.log(`[${timestamp}] ${method} ${url}`);
// No olvides llamar a next() para continuar
next();
};
module.exports = loggerMiddleware;
next() o envie una respuesta. Olvidar esto provocará que la petición quede colgada indefinidamente.Aplicando Middlewares en tu Aplicación
Ahora veamos cómo usar este middleware en tu servidor Express:
// app.js
const express = require('express');
const loggerMiddleware = require('./middleware/logger');
const app = express();
// Middleware a nivel de aplicación - se ejecuta en TODAS las peticiones
app.use(loggerMiddleware);
// También puedes pasar opciones a tu middleware
app.use('/api', loggerMiddleware);
app.get('/users', (req, res) => {
res.json({ users: [] });
});
app.listen(3000, () => {
console.log('Servidor corriendo en puerto 3000');
});
Middlewares con Parámetros
A veces necesitas que tu middleware acepte configuraciones. Para esto, puedes factory functions:
// middleware/validateAuth.js
const validateAuth = (options = {}) => {
const { required = true, roles = [] } = options;
return (req, res, next) => {
const token = req.headers.authorization;
if (!token && required) {
return res.status(401).json({
error: 'Token de autenticación requerido'
});
}
// Lógica de validación aquí...
req.user = { id: 1, role: 'admin' }; // Simulación
next();
};
};
module.exports = validateAuth;
// Uso:
app.get('/admin', validateAuth({ roles: ['admin'] }), (req, res) => {
res.json({ message: 'Zona administrativa' });
});
Middlewares de Validación de Datos
Un caso de uso muy común es la validación de datos de entrada. Veamos un middleware para validar el cuerpo de una solicitud:
// middleware/validateUser.js
const validateUser = (req, res, next) => {
const { name, email, password } = req.body;
const errors = [];
// Validar nombre
if (!name || typeof name !== 'string' || name.length < 2) {
errors.push('El nombre debe tener al menos 2 caracteres');
}
// Validar email con expresión regular
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
errors.push('Email inválido');
}
// Validar contraseña
if (!password || password.length < 8) {
errors.push('La contraseña debe tener al menos 8 caracteres');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
};
// Uso en ruta
app.post('/users', validateUser, (req, res) => {
// Aquí ya sabes que los datos son válidos
const user = req.body;
res.status(201).json({ message: 'Usuario creado', user });
});
- Define el middleware con acceso a req, res y next.
- Extrae los datos a validar del objeto request.
- Aplica tus reglas de validación.
- Si hay errores, responde inmediatamente con status 400.
- Si todo está bien, llama a next() para continuar.
Middlewares Asíncronos
En aplicaciones reales, frecuentemente necesitarás realizar operaciones asíncronas dentro de tus middlewares:
// middleware/checkDatabase.js
const checkDatabase = async (req, res, next) => {
try {
// Simulación de consulta a base de datos
const dbConnected = await verifyDatabaseConnection();
if (!dbConnected) {
return res.status(503).json({
error: 'Servicio de base de datos no disponible'
});
}
req.dbConnected = true;
next();
} catch (error) {
next(error); // Pasa el error al siguiente middleware de errores
}
};
module.exports = checkDatabase;
next(error). Express lo capturará y lo enviará al middleware de manejo de errores.Middleware de Manejo de Errores
Express tiene un tipo especial de middleware con cuatro parámetros que se encarga de manejar errores:
// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
console.error('Error:', err.stack);
const statusCode = err.statusCode || 500;
const message = err.message || 'Error interno del servidor';
res.status(statusCode).json({
error: {
message,
status: statusCode,
timestamp: new Date().toISOString(),
path: req.path
}
});
};
module.exports = errorHandler;
// Uso - debe ser el ÚLTIMO middleware registrado
app.use(errorHandler);
Un middleware de manejo de errores debe tener siempre cuatro parámetros. Express reconoce esta firma especial y sabe que se trata de un handler de errores.
Encadenando Middlewares
Puedes aplicar múltiples middlewares a una misma ruta. Se ejecutarán en orden:
app.post(
'/products',
validateProduct, // 1. Valida los datos
checkPermissions, // 2. Verifica permisos del usuario
checkDatabase, // 3. Verifica conexión a BD
async (req, res) => { // 4. Handler final
const product = await ProductService.create(req.body);
res.status(201).json(product);
}
);
Middlewares de Terceros Útiles
Ver másExisten muchos middlewares populares que puedes instalar via npm:
- morgan: Logger HTTP para desarrollo y producción.
- cors: Habilita CORS (Cross-Origin Resource Sharing).
- helmet: Configura headers de seguridad HTTP.
- express-rate-limit: Limita las solicitudes repetidas.
- express-validator: Validación y sanitización de datos.
// Instalación
npm install morgan cors helmet express-rate-limit
// Uso básico
const morgan = require('morgan');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
app.use(morgan('dev'));
app.use(cors());
app.use(helmet());
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100 // límite de 100 solicitudes por ventana
});
app.use('/api', limiter);
Organización de Middlewares
Para proyectos grandes, organiza tus middlewares en carpetas temáticas:
/src
/middleware
/auth
validateToken.js
requireRole.js
/validation
validateUser.js
validateProduct.js
/security
rateLimiter.js
cors.js
/logging
logger.js
requestId.js
errorHandler.js
app.js
Resumen Práctico
| Patrón | Sintaxis | Uso |
|---|---|---|
| Básico | (req, res, next) => {} |
Logging, parsing simple |
| Con opciones | (options) => (req, res, next) => {} |
Configuraciones dinámicas |
| De errores | (err, req, res, next) => {} |
Manejo centralizado de errores |
| Async | async (req, res, next) => {} |
Operaciones con BD o APIs externas |
Los middlewares son el alma de Express. Dominar su uso te permitirá crear APIs robustas, mantenibles y escalables. Practica creando tus propios middlewares y verás cómo tu código se vuelve mucho más limpio y modular.
¿Cuál es el orden correcto de los parámetros en un middleware de manejo de errores de Express?
- A) req, res, next, error
- B) error, req, res, next
- C) req, error, res, next
- D) next, req, res, error
¿Qué sucede si un middleware no llama a next() ni envía una respuesta?
- A) El servidor se reinicia automáticamente
- B) La petición queda colgada indefinidamente
- C) Express ignora el middleware automáticamente
- D) Se genera un error 500 inmediatamente