Introduction
Client-supplied data can never be trusted at face value: fields may be missing, malformed, or malicious. Data validation is the practice of checking incoming request data against explicit rules — required fields, correct types, value ranges, formats like email — before it reaches business logic or the database. In Express, validation is commonly implemented as middleware using a schema library such as Joi or express-validator, which centralizes rules and produces clear 400-level error responses when data fails to meet them.
Cricket analogy: A team's team-sheet can't be trusted blindly — the selectors check that each name is a registered player, the batting order is complete, and no duplicate is submitted, exactly like a Joi schema checking required fields before Express accepts a request.
Syntax
const express = require('express');
const Joi = require('joi');
const app = express();
app.use(express.json());
const createUserSchema = Joi.object({
name: Joi.string().trim().min(2).max(80).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(130).optional(),
});
function validateBody(schema) {
return (req, res, next) => {
const { error, value } = schema.validate(req.body, { abortEarly: false });
if (error) {
return res.status(400).json({
error: 'Validation failed',
details: error.details.map((d) => d.message),
});
}
req.body = value;
next();
};
}
app.post('/users', validateBody(createUserSchema), (req, res) => {
res.status(201).json({ message: 'User is valid', data: req.body });
});Explanation
The Joi.object() schema declares each expected field, its type, and constraints such as min, max, email, and required(). The validateBody factory function returns Express middleware that runs schema.validate() against req.body; abortEarly: false collects every validation error instead of stopping at the first one, which gives clients a complete picture of what to fix. If validation fails, the middleware short-circuits the request with a 400 Bad Request and a details array, so the route handler itself only ever sees data that already satisfies the schema — keeping validation concerns separate from business logic.
Cricket analogy: A selection panel's checklist specifies each field precisely — age must be within range, batting average above a minimum, email format correct — and reviews the entire team sheet at once rather than stopping at the first bad entry, giving the captain a full list of fixes needed.
Example
// Request 1: valid payload
// POST /users
// { "name": "Grace Hopper", "email": "grace@example.com", "age": 85 }
// Request 2: invalid payload
// POST /users
// { "name": "G", "email": "not-an-email" }Output
Request 1 passes validation and the route responds with status 201 and {"message":"User is valid","data":{"name":"Grace Hopper","email":"grace@example.com","age":85}}. Request 2 fails validation because 'G' is shorter than the minimum length of 2 and 'not-an-email' is not a valid email format, so the middleware responds with status 400 and {"error":"Validation failed","details":["\"name\" length must be at least 2 characters long","\"email\" must be a valid email"]}, never reaching the route handler or the database.
Cricket analogy: Submitting Sachin Tendulkar's registration with a full name and valid contact passes and confirms 201, while submitting 'S.' with a malformed contact fails validation and returns 400 listing both the too-short name and the bad email format, never reaching the selection committee.
Key Takeaways
- Never trust client input directly — validate types, formats, and required fields before using them.
- Implement validation as reusable Express middleware so route handlers stay focused on business logic.
- Use abortEarly: false to collect and return all validation errors at once, improving the developer/client experience.
- Respond with 400 Bad Request and clear error details when validation fails.
- Validation complements but does not replace database-level constraints like unique indexes or NOT NULL columns.
Practice what you learned
1. What is the main purpose of data validation middleware in Express?
2. What HTTP status code should be returned when request data fails validation?
3. What does setting { abortEarly: false } do in a Joi schema.validate() call?
4. Why is it good practice to implement validation as reusable middleware rather than inline checks in each route?
Was this page helpful?
You May Also Like
CRUD Operations in Express
Build complete Create, Read, Update, and Delete route handlers in Express using a database model.
Connecting to MongoDB with Mongoose
Learn how to connect an Express app to MongoDB using Mongoose schemas, models, and connection management.
Error Handling in Express
Master synchronous and asynchronous error handling in Express using 4-argument error middleware.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics