Validaciones con middleware en Express
En esta clase metemos una mejora muy profesional a nuestra API - validar lo que entra antes de crear o actualizar recursos. La idea es simple: si el cliente manda datos inválidos, no llegamos al controlador. Cortamos antes con un 400 y un mensaje útil.
¿Dónde validamos?
La validación la vamos a aplicar en las rutas, usando un middleware que se ejecuta antes del handler final. Así, cada endpoint decide qué validación necesita y el controlador se centra solo en la lógica de negocio.
Dos casos típicos: crear vs actualizar
- POST: validación completa (todos los campos requeridos)
- PATCH: validación parcial (solo lo que venga en el body)
Para eso reutilizamos dos esquemas:
validateJobpara creaciónvalidatePartialJobpara actualización parcial
Middleware: validateCreate
Creamos una función validateCreate con la firma típica req, res, next, y validamos con safeParse para saber si ha ido bien sin tirar excepciones.
// routes/jobs.js (o en middlewares/validateJob.js si lo quieres separar)
import { validateJob } from '../schemas/jobs.js'
export function validateCreate(req, res, next) {
const result = validateJob(req.body)
if (result.success) {
// Opcional pero muy útil: dejamos el body "limpio" y tipado por el schema
req.body = result.data
return next()
}
return res.status(400).json({
message: 'Invalid request',
errors: result.error.errors,
})
}
Esto hace dos cosas clave:
- Si es válido -
next()y seguimos al controlador. - Si no es válido - 400 + lista de errores detallada.
Middleware: validateUpdate
Para el PATCH, lo mismo, pero con el schema parcial:
import { validatePartialJob } from '../schemas/jobs.js'
export function validateUpdate(req, res, next) {
const result = validatePartialJob(req.body)
if (result.success) {
req.body = result.data
return next()
}
return res.status(400).json({
message: 'Invalid request',
errors: result.error.errors,
})
}
Conectándolo en las rutas
Ahora ponemos el middleware justo antes del handler del endpoint:
import { Router } from 'express'
import { createJob, updateJob } from '../controllers/jobs.js'
import { validateCreate, validateUpdate } from '../middlewares/jobsValidation.js'
const router = Router()
router.post('/', validateCreate, createJob)
router.patch('/:id', validateUpdate, updateJob)
export default router
Así queda clarísimo el flujo:
- entra request
- valida
- si pasa, ejecuta el controlador
- si no pasa, responde con error
Bonus: validación + transformación
Una idea interesante es que el middleware no solo valide, también puede transformar datos para normalizarlos. Por ejemplo:
- pasar technologies a lowercase
- mapear campos
- limpiar strings
Y como reasignamos req.body = result.data, el controlador recibe los datos ya listos para trabajar.
Probándolo rápido
La validación se nota al instante:
- si quitas un campo obligatorio como
title, devuelveInvalid requesty no crea el recurso - si mandas un tipo incorrecto (por ejemplo un número donde esperaba texto), también falla
Lo que hemos aprendido
- Dónde colocar las validaciones - en rutas con middleware
- Cómo diferenciar validación total (POST) y parcial (PATCH)
- Cómo usar
safeParsepara obtenersuccess,datayerrors - Cómo devolver 400 con mensajes útiles
- Por qué esto también se tiene que testear en la API