Why do you have to validate the client's input, and what is the difference between responding 400 and 422?
Show answer — try answering out loud first
You must always validate what comes from the client because you can't trust external input: it may arrive incomplete, with incorrect types or malicious. If the request is malformed or missing a required field, you respond 400 Bad Request. If the structure is correct but the data is semantically invalid, you respond 422 Unprocessable Entity. It's also worth sanitizing (trimming whitespace, normalizing) and not trusting the types the client sends.
Never trust the client's input. Even if your frontend validates, anyone can call your API directly with curl or Postman. Validation on the server is what really protects your data and your logic.
What to check when validating:
- Required fields present. If a
titulois required and doesn't come, reject the request. - Correct types. JSON may send a number as text (
"5") or an object where you expected a string. Don't assume the type: check it or convert it in a controlled way. - Ranges and formats. An email that looks like an email, a positive age, a valid date.
400 vs 422
- 400 Bad Request: the request is malformed at the syntax or structure level. Broken JSON, a required field is missing, the body cannot be interpreted.
- 422 Unprocessable Entity: the request is well-formed (the server understands it), but the data doesn't make sense according to the business rules. For example, an email with an impossible format or an end date earlier than the start date.
Mental rule: 400 = I don't understand you; 422 = I understand you, but I can't accept that.
Basic sanitization
Sanitizing is cleaning the input before using it:
- Trim whitespace with
trim()so that" Ana "doesn't come in as is. - Normalize (for example, lowercasing emails).
- Don't trust the types: convert and check explicitly.
In real projects libraries like zod or express-validator are usually used to declare the rules cleanly and avoid repetitive manual validations. Here we'll do it by hand to see the idea clearly.
const express = require('express');
const app = express();
app.use(express.json());
// POST /notas: the "titulo" field is required
app.post('/notas', (req, res) => {
const { titulo } = req.body;
// 1) Validate presence and type: it must exist and be a string
if (typeof titulo !== 'string') {
return res.status(400).json({
error: 'El campo "titulo" es obligatorio y debe ser texto',
});
}
// 2) Sanitize: trim excess whitespace
const tituloLimpio = titulo.trim();
// 3) Validate content: it can't be empty after the trim
if (tituloLimpio.length === 0) {
return res.status(400).json({
error: 'El campo "titulo" no puede estar vacío',
});
}
// 4) Business rule: maximum 100 characters.
// The structure is valid, but the value doesn't comply -> 422
if (tituloLimpio.length > 100) {
return res.status(422).json({
error: 'El "titulo" no puede superar los 100 caracteres',
});
}
// If we get here, the data is valid and clean
const nota = { id: Date.now(), titulo: tituloLimpio };
res.status(201).json(nota);
});
app.listen(3000);
# Missing title -> we expect 400
curl -i -X POST http://localhost:3000/notas \
-H "Content-Type: application/json" \
-d '{}'
# Valid title -> we expect 201
curl -i -X POST http://localhost:3000/notas \
-H "Content-Type: application/json" \
-d '{"titulo":" Mi primera nota "}'
Trusting that the frontend already validated. The frontend improves the experience, but it's not a security barrier: anyone can bypass it by calling the API directly. Real validation goes on the server.
Another mistake is trusting the JSON types: assuming that edad is a number when the client sent "veinte", or that a field exists without checking it, which causes errors like reading properties of undefined.
"I always validate the client's input on the server, because even if the frontend validates, anyone can call the API directly and I can't trust external data. I check that the required fields are present, that the types are correct and that the values make sense. For errors I distinguish between 400 and 422: I use 400 when the request is malformed or a required field is missing, that is, I don't understand it; and I use 422 when the structure is valid but the data doesn't comply with a business rule, that is, I understand it but I can't accept it. I also sanitize the basics, like trimming whitespace with trim. In real projects I back this up with libraries like zod or express-validator so I don't repeat validations by hand."
This endpoint assumes precio is always a number. What problem does it have and how would you fix it?
app.post('/productos', (req, res) => {
const total = req.body.precio * 1.16; // añade IVA
res.status(201).json({ total });
});
See answer
The problem is that it trusts the type of the input. If the client sends precio: "abc" or doesn't send it, req.body.precio * 1.16 gives NaN, and the endpoint returns a meaningless total with a 201 (as if everything had gone fine).
Fix: validate before operating.
app.post('/productos', (req, res) => {
const precio = Number(req.body.precio);
// Reject if it didn't come, isn't a number or is negative
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 });
});