Trusting incoming request data without validation is one of the most common sources of bugs and security issues in APIs. This lesson covers validating request bodies, params, and queries before they reach your business logic.
Why Validate Requests?
Client input can be missing, the wrong type, out of range, or maliciously crafted. Validating requests at the edge of your API, before any business logic or database call runs, ensures downstream code can trust the shape of the data it receives, and lets you return a clear 400 Bad Request instead of a confusing crash.
For small APIs, manual checks are reasonable. As the number of fields and rules grows, schema-based validation libraries (like zod or joi) express validation rules declaratively and produce consistent, detailed error messages automatically.
app.post('/users', (req, res) => {
const { email, age } = req.body;
if (typeof email !== 'string' || !email.includes('@')) {
return res.status(400).json({ error: 'A valid email is required' });
}
if (typeof age !== 'number' || age < 0) {
return res.status(400).json({ error: 'Age must be a non-negative number' });
}
res.status(201).json({ email, age });
});
Each validation check returns early with a 400 status, business logic below this point can safely assume email and age are valid.
Schema-Based Validation With Zod
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email(),
age: z.number().nonnegative(),
});
const result = userSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.issues });
}
Schema libraries let you declare validation rules once and reuse them across routes.
safeParse() (Zod) returns a result object instead of throwing, ideal for handling in route code.
Validation errors should map to 400 Bad Request, not 500 Internal Server Error.
Validate req.body, req.params, and req.query separately when each has different rules.
Request Validation Cheatsheet
Where and how to validate different parts of a request.
Source
Example Rule
Failure Status
req.body
Required fields, types, string formats
400
req.params
ID must be a valid number/UUID
400
req.query
page/limit must be positive integers
400
req.headers
Required API key or content type
400/401
Business rule failure
e.g. "email already registered"
409
Turning Validation Into Middleware
Rather than repeating validation logic inside every route handler, a small reusable middleware factory can validate a request against a given schema and short-circuit with a 400 before the route handler ever runs.
This keeps createUser focused purely on business logic, trusting that req.body has already been validated and normalized.
Validation vs Sanitization
Validation checks whether data meets the required rules and rejects it if not. Sanitization goes further, modifying input into a safe or normalized form (trimming whitespace, escaping HTML, lowercasing an email) rather than just rejecting it outright. Real APIs typically need both.
Common Mistakes
Trusting req.body fields directly without checking their type or presence before using them.
Returning a 500 error for invalid input instead of a 400, confusing client errors with server errors.
Duplicating the same validation logic across many routes instead of extracting reusable middleware.
Validating only the request body while ignoring params and query strings that also need checks.
Key Takeaways
Validate all incoming request data (body, params, query) before it reaches business logic.
Return 400 Bad Request with a clear message for invalid input, not a generic 500.
Schema-based validation libraries scale better than manual checks as field counts grow.
Validation middleware keeps route handlers focused purely on business logic.
Pro Tip
Adopt a schema validation library (Zod, Joi, or similar) as soon as an endpoint has more than two or three fields to check. The upfront cost is small, and the payoff, consistent error messages and far less manual if logic, compounds quickly across a growing API.
You now validate requests properly. Next, learn API versioning strategies for evolving an API without breaking existing clients.