Object-based validation approach using checkSchema for complex validation rules and reusable validation logic. Schema validation provides a declarative way to define validation rules for entire objects.
Creates validation middleware from a schema object definition.
/**
* Creates validation middleware from schema object
* @param schema - Object mapping field names to validation schemas
* @param defaultLocations - Default request locations to check if not specified in schema
* @returns Array of ValidationChain instances
*/
function checkSchema(schema: Schema, defaultLocations?: Location[]): ValidationChain[];
interface Schema {
[field: string]: ParamSchema;
}
type Location = 'body' | 'cookies' | 'headers' | 'params' | 'query';Usage Examples:
import { checkSchema } from "express-validator";
// Basic schema validation
const userSchema = checkSchema({
name: {
trim: true,
isLength: { options: { min: 2, max: 50 } },
errorMessage: 'Name must be between 2 and 50 characters'
},
email: {
isEmail: true,
normalizeEmail: true,
errorMessage: 'Must be a valid email address'
},
age: {
isInt: { options: { min: 18, max: 120 } },
toInt: true,
errorMessage: 'Age must be between 18 and 120'
}
});
// Use in Express route
app.post('/user', userSchema, (req, res) => {
// Handle validation results
});Defines validation and sanitization rules for individual fields.
interface ParamSchema {
// Location specification
in?: Location | Location[];
// Existence options
exists?: boolean | {
errorMessage?: string;
checkNull?: boolean;
checkFalsy?: boolean;
};
// Optional field configuration
optional?: boolean | {
nullable?: boolean;
checkFalsy?: boolean;
};
// Array validation
isArray?: boolean | {
min?: number;
max?: number;
};
// Conditional validation
if?: (value: any, meta: Meta) => boolean;
// Error handling
errorMessage?: string;
bail?: boolean;
// Negation
not?: boolean;
// All validator methods (subset shown)
isEmail?: boolean | EmailOptions;
isInt?: boolean | IntOptions;
isLength?: { min?: number; max?: number };
isURL?: boolean | URLOptions;
matches?: string | RegExp;
custom?: CustomValidator;
// All sanitizer methods (subset shown)
trim?: boolean | string;
escape?: boolean;
normalizeEmail?: boolean | NormalizeEmailOptions;
toInt?: boolean | number;
toFloat?: boolean;
customSanitizer?: CustomSanitizer;
}Specify multiple request locations for field validation.
const authSchema = checkSchema({
token: {
// Check headers and query for token
in: ['headers', 'query'],
exists: true,
isLength: { min: 20 },
errorMessage: 'Valid token required'
},
userId: {
// Check params and body for userId
in: ['params', 'body'],
isUUID: true,
errorMessage: 'Valid user ID required'
}
});Handle optional fields with various null/undefined behaviors.
const profileSchema = checkSchema({
bio: {
optional: true, // Field is optional
trim: true,
isLength: { max: 500 }
},
avatar: {
optional: { nullable: true }, // Allow null values
isURL: true
},
preferences: {
optional: { checkFalsy: true }, // Treat falsy values as missing
isJSON: true
}
});Validate array fields and their elements.
const orderSchema = checkSchema({
items: {
isArray: { min: 1, max: 10 },
errorMessage: 'Must provide 1-10 items'
},
'items.*.productId': {
isUUID: true,
errorMessage: 'Each item must have valid product ID'
},
'items.*.quantity': {
isInt: { min: 1, max: 100 },
toInt: true,
errorMessage: 'Quantity must be between 1 and 100'
},
'items.*.price': {
isDecimal: { decimal_digits: '2' },
toFloat: true,
errorMessage: 'Price must be valid decimal'
}
});Apply validation rules conditionally based on other field values.
const paymentSchema = checkSchema({
paymentMethod: {
isIn: { options: [['card', 'paypal', 'bank']] },
errorMessage: 'Invalid payment method'
},
cardNumber: {
if: (value, { req }) => req.body.paymentMethod === 'card',
isCreditCard: true,
errorMessage: 'Valid card number required for card payments'
},
paypalEmail: {
if: (value, { req }) => req.body.paymentMethod === 'paypal',
isEmail: true,
errorMessage: 'Valid PayPal email required'
},
bankAccount: {
if: (value, { req }) => req.body.paymentMethod === 'bank',
matches: /^\d{8,12}$/,
errorMessage: 'Valid bank account number required'
}
});Integrate custom validation logic within schema definitions.
const registrationSchema = checkSchema({
username: {
trim: true,
isLength: { min: 3, max: 20 },
matches: /^[a-zA-Z0-9_]+$/,
custom: async (value) => {
const exists = await User.findByUsername(value);
if (exists) {
throw new Error('Username already taken');
}
return true;
}
},
password: {
isStrongPassword: {
options: {
minLength: 8,
minLowercase: 1,
minUppercase: 1,
minNumbers: 1,
minSymbols: 1
}
}
},
confirmPassword: {
custom: (value, { req }) => {
if (value !== req.body.password) {
throw new Error('Password confirmation does not match');
}
return true;
}
}
});Validate nested object properties using dot notation.
const addressSchema = checkSchema({
'address.street': {
trim: true,
notEmpty: true,
errorMessage: 'Street address is required'
},
'address.city': {
trim: true,
isAlpha: true,
errorMessage: 'City must contain only letters'
},
'address.zipCode': {
matches: /^\d{5}(-\d{4})?$/,
errorMessage: 'Invalid ZIP code format'
},
'address.country': {
isISO31661Alpha2: true,
errorMessage: 'Must be valid country code'
}
});Customize error messages at field and rule levels.
const validationSchema = checkSchema({
email: {
isEmail: {
errorMessage: 'Please enter a valid email address'
},
normalizeEmail: true
},
age: {
isInt: {
options: { min: 18, max: 100 },
errorMessage: 'Age must be a number between 18 and 100'
},
toInt: true
},
// Global error message for the field
phone: {
isMobilePhone: { options: 'en-US' },
errorMessage: 'Please provide a valid US phone number'
}
});Define reusable schema components for consistent validation.
// Reusable field schemas
const commonFields = {
email: {
isEmail: true,
normalizeEmail: true,
errorMessage: 'Valid email required'
},
password: {
isStrongPassword: {
options: { minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1 }
},
errorMessage: 'Password must be strong'
}
};
// User registration schema
const registerSchema = checkSchema({
...commonFields,
firstName: {
trim: true,
isLength: { min: 2, max: 50 },
errorMessage: 'First name must be 2-50 characters'
},
lastName: {
trim: true,
isLength: { min: 2, max: 50 },
errorMessage: 'Last name must be 2-50 characters'
}
});
// User login schema (reuses email field)
const loginSchema = checkSchema({
email: commonFields.email,
password: {
notEmpty: true,
errorMessage: 'Password is required'
}
});