One of the most critical principles in web security and stability is "never trust user input." Invalid data can cause unexpected crashes, corrupt your database, and open security holes. Request validation is the process of enforcing rules on the data sent by clients.

While you could write a series of if statements, this quickly becomes messy. A much better approach is to use a schema validation library like Zod or Joi. We'll focus on Zod for its excellent TypeScript support and modern API.

1. Setting Up Zod

First, install Zod in your project:

Bash


npm install zod

2. Defining a Schema

With Zod, you define a "schema"—an object that describes the shape and rules of your expected data. Let's create a schema for a new user registration endpoint, which expects an email and a password.

JavaScript


const { z } = require('zod');

// Define the schema for the user registration request body
const registerSchema = z.object({
  email: z.string().email({ message: "Invalid email address" }),
  password: z.string().min(8, { message: "Password must be at least 8 characters long" }),
  fullName: z.string().optional(), // fullName is an optional string
});

This schema enforces that:

  • email must be a string and in a valid email format.
  • password must be a string and have a minimum length of 8 characters.
  • fullName is not required, but if it exists, it must be a string.

3. Creating a Validation Middleware

The best way to apply validation is through a middleware. We can create a reusable middleware that takes a schema and validates the request body against it.

JavaScript


const validate = (schema) => (req, res, next) => {
  try {
    // .parse() will throw an error if validation fails
    schema.parse(req.body);
    next();
  } catch (error) {
    // If validation fails, send a 400 Bad Request response
    // with the detailed error messages from Zod.
    res.status(400).json({
      status: 'error',
      message: 'Invalid request data',
      errors: error.errors, // Zod provides a detailed errors array
    });
  }
};

module.exports = validate;

4. Applying the Middleware to a Route

Now, we can import our schema and validation middleware and apply it directly to our route handler.

JavaScript


const express = require('express');
const { z } = require('zod');
const validate = require('./validateMiddleware'); // Assume the middleware is in this file

const app = express();
app.use(express.json());

// Define the schema
const registerSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

// Apply the validation middleware before the route handler
app.post('/register', validate(registerSchema), (req, res) => {
  // If we get here, the data is valid!
  const { email, password } = req.body;

  // ... proceed with user creation logic ...

  res.status(201).json({ message: 'User registered successfully!' });
});

app.listen(3000, () => console.log('Server is running...'));

If a client tries to send a request with an invalid email or a short password, our middleware will automatically catch it and send back a helpful 400 Bad Request error response, preventing the invalid data from ever reaching our core application logic.