Skip to content

express-validator Tutorial

express-validator is a popular middleware wrapper around the validator.js library, built specifically to fit naturally into the Express middleware pipeline. This lesson covers its core API.

How express-validator Works

express-validator exposes chainable validation functions, body(), param(), query(), that you pass as middleware directly into a route. Each chain describes what a specific field must look like, and any errors accumulate without stopping the request immediately.

After the validation chains run, you call validationResult(req) inside your route handler (or a small wrapper middleware) to check whether anything failed.

const { body, validationResult } = require('express-validator');

app.post(
  '/users',
  body('email').isEmail().withMessage('Invalid email'),
  body('age').isInt({ min: 0 }).withMessage('Age must be a positive integer'),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    res.status(201).json(req.body);
  }
);

Each body() call is its own middleware in the chain; validationResult(req) collects everything they found.

Common Validation Chain Methods

body('field')
  .trim()               // sanitizer
  .notEmpty()            // validator
  .isLength({ min: 3 })  // validator
  .escape()              // sanitizer
  .withMessage('Custom error message');
  • Validators (isEmail, isInt, notEmpty) check the value and record an error if it fails.
  • Sanitizers (trim, escape, toInt) transform the value in place.
  • withMessage() attaches a custom error message to the preceding check.
  • param(), query(), and body() all share the same chainable API for different input sources.

express-validator Cheatsheet

The validators and sanitizers you will use most often.

Method Example Purpose
body() body('email').isEmail() Validate a request body field
param() param('id').isInt() Validate a route parameter
query() query('page').isInt({ min: 1 }) Validate a query string field
isEmail() body('email').isEmail() Checks valid email format
isLength() body('password').isLength({ min: 8 }) Checks string length
trim() body('name').trim() Removes surrounding whitespace
validationResult() validationResult(req) Collects all validation errors

Reusable Validation-Handling Middleware

Checking validationResult() manually in every route is repetitive. A small reusable middleware can centralize this check across all your validated routes.

const { validationResult } = require('express-validator');

function handleValidation(req, res, next) {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  next();
}

app.post(
  '/users',
  body('email').isEmail(),
  body('age').isInt({ min: 0 }),
  handleValidation,
  controller.createUser
);

Validating Nested and Array Fields

express-validator supports dot and bracket notation for validating nested objects and array items directly.

body('address.city').notEmpty(),
body('tags').isArray({ min: 1 }),
body('tags.*').isString().trim(),

Common Mistakes

  • Forgetting to call validationResult(req) after the validation chains, silently ignoring all detected errors.
  • Placing validation chains after the route handler instead of before it in the middleware list.
  • Mixing up sanitizers and validators, expecting .trim() to reject empty strings (it doesn't, pair it with .notEmpty()).
  • Not centralizing the validationResult() check, leading to inconsistent error handling across routes.

Key Takeaways

  • express-validator provides chainable middleware for validating and sanitizing request data.
  • body(), param(), and query() target different parts of the request.
  • validationResult(req) collects accumulated errors for you to check and respond to.
  • A small reusable middleware avoids repeating the same error-check logic in every route.

Pro Tip

Chain sanitizers before validators when order matters (e.g. .trim().notEmpty()), so validation runs against the cleaned-up value rather than raw, whitespace-padded input.