¿Por qué hay que validar el input del cliente y cuál es la diferencia entre responder 400 y 422?
Ver respuesta — intenta responderla en voz alta primero
Hay que validar siempre lo que llega del cliente porque no se puede confiar en el input externo: puede venir incompleto, con tipos incorrectos o malicioso. Si la petición está mal formada o le falta un campo obligatorio, se responde 400 Bad Request. Si la estructura es correcta pero los datos son semánticamente inválidos, se responde 422 Unprocessable Entity. Además conviene sanitizar (recortar espacios, normalizar) y no confiar en los tipos que manda el cliente.
Nunca confíes en el input del cliente. Aunque tu frontend valide, cualquiera puede llamar a tu API directamente con curl o Postman. La validación en el servidor es la que protege de verdad tus datos y tu lógica.
Qué revisar al validar:
- Campos obligatorios presentes. Si un
tituloes requerido y no viene, rechaza la petición. - Tipos correctos. El JSON puede mandar un número como texto (
"5") o un objeto donde esperabas un string. No asumas el tipo: compruébalo o conviértelo de forma controlada. - Rangos y formatos. Un email con forma de email, una edad positiva, una fecha válida.
400 vs 422
- 400 Bad Request: la petición está mal formada a nivel de sintaxis o estructura. JSON roto, falta un campo obligatorio, el body no se puede interpretar.
- 422 Unprocessable Entity: la petición está bien formada (el servidor la entiende), pero los datos no tienen sentido según las reglas del negocio. Por ejemplo, un email con formato imposible o una fecha de fin anterior a la de inicio.
Regla mental: 400 = no te entiendo; 422 = te entiendo, pero eso no lo puedo aceptar.
Sanitización básica
Sanitizar es limpiar el input antes de usarlo:
- Recortar espacios con
trim()para que" Ana "no entre como está. - Normalizar (por ejemplo, pasar emails a minúsculas).
- No confiar en los tipos: convierte y comprueba explícitamente.
En proyectos reales se suelen usar librerías como zod o express-validator para declarar las reglas de forma limpia y evitar validaciones manuales repetitivas. Aquí lo haremos a mano para ver la idea con claridad.
const express = require('express');
const app = express();
app.use(express.json());
// POST /notas: el campo "titulo" es obligatorio
app.post('/notas', (req, res) => {
const { titulo } = req.body;
// 1) Validar presencia y tipo: debe existir y ser un string
if (typeof titulo !== 'string') {
return res.status(400).json({
error: 'El campo "titulo" es obligatorio y debe ser texto',
});
}
// 2) Sanitizar: recortar espacios sobrantes
const tituloLimpio = titulo.trim();
// 3) Validar contenido: no puede quedar vacío tras el trim
if (tituloLimpio.length === 0) {
return res.status(400).json({
error: 'El campo "titulo" no puede estar vacío',
});
}
// 4) Regla de negocio: máximo 100 caracteres.
// La estructura es válida, pero el valor no cumple -> 422
if (tituloLimpio.length > 100) {
return res.status(422).json({
error: 'El "titulo" no puede superar los 100 caracteres',
});
}
// Si llegamos aquí, el dato es válido y está limpio
const nota = { id: Date.now(), titulo: tituloLimpio };
res.status(201).json(nota);
});
app.listen(3000);
# Falta el título -> esperamos 400
curl -i -X POST http://localhost:3000/notas \
-H "Content-Type: application/json" \
-d '{}'
# Título válido -> esperamos 201
curl -i -X POST http://localhost:3000/notas \
-H "Content-Type: application/json" \
-d '{"titulo":" Mi primera nota "}'
Confiar en que el frontend ya validó. El frontend mejora la experiencia, pero no es una barrera de seguridad: cualquiera puede saltárselo llamando a la API directamente. La validación de verdad va en el servidor.
Otro error es confiar en los tipos del JSON: dar por hecho que edad es un número cuando el cliente mandó "veinte", o que un campo existe sin comprobarlo, lo que provoca errores como leer propiedades de undefined.
"Valido siempre el input del cliente en el servidor, porque aunque el frontend valide, cualquiera puede llamar a la API directamente y no puedo confiar en datos externos. Compruebo que los campos obligatorios estén presentes, que los tipos sean los correctos y que los valores tengan sentido. Para los errores distingo entre 400 y 422: uso 400 cuando la petición está mal formada o falta un campo obligatorio, es decir, no la entiendo; y uso 422 cuando la estructura es válida pero los datos no cumplen una regla de negocio, es decir, la entiendo pero no la puedo aceptar. También sanitizo lo básico, como recortar espacios con trim. En proyectos reales apoyo esto con librerías como zod o express-validator para no repetir validaciones a mano."
Este endpoint asume que precio siempre es un número. ¿Qué problema tiene y cómo lo arreglarías?
app.post('/productos', (req, res) => {
const total = req.body.precio * 1.16; // añade IVA
res.status(201).json({ total });
});
Ver respuesta
El problema es que confía en el tipo del input. Si el cliente manda precio: "abc" o no lo manda, req.body.precio * 1.16 da NaN, y el endpoint devuelve un total sin sentido con un 201 (como si todo hubiera ido bien).
Arreglo: validar antes de operar.
app.post('/productos', (req, res) => {
const precio = Number(req.body.precio);
// Rechaza si no vino, no es número o es negativo
if (!Number.isFinite(precio) || precio < 0) {
return res.status(400).json({
error: 'El campo "precio" debe ser un número positivo',
});
}
const total = precio * 1.16;
res.status(201).json({ total });
});