Zod Express Request Validation Middleware

raw JSON →
0.0.8 verified Thu Apr 23 auth: no javascript

zod-express is an Express.js middleware designed for robust request validation using Zod schemas. Currently at version 0.0.8, this package provides functions like `validateRequest` and `processRequest` to ensure incoming `req.body`, `req.query`, and `req.params` conform to predefined Zod schemas, offering compile-time type safety in TypeScript environments. It recently introduced support for asynchronous validators. `zod-express` is a fork of `zod-express-middleware`, aiming to provide a maintained solution for this use case. Its release cadence appears to be driven by new features or bug fixes, rather than a strict schedule, as indicated by the recent `0.0.8` release adding async validation. Key differentiators include its focus on strong typing with Zod and the flexibility to either just validate or validate and transform/process request data.

error TypeError: validateRequest is not a function
cause Incorrect import statement or attempting to use CommonJS `require()` syntax with an ESM-first package.
fix
Ensure you are using import { validateRequest } from 'zod-express'; at the top of your file. If using CommonJS, check your tsconfig.json for "module": "NodeNext" or "type": "module" in package.json.
error ZodError: Invalid input
cause The incoming request data (body, query, or params) does not conform to the defined Zod schema.
fix
Inspect the err.errors array within your Express error-handling middleware to get detailed validation messages. Adjust the client-side data or refine your Zod schema to match expected inputs.
error Property 'bodyKey' does not exist on type 'Request'
cause This TypeScript error occurs when `req.body` (or `req.query`, `req.params`) is accessed after `validateRequest` but TypeScript hasn't correctly inferred the new, validated type. This often happens if the middleware isn't correctly typed or if the schema isn't fully captured by the `Request` type.
fix
Ensure your Express app's request handler is correctly typed to leverage the zod-express middleware's type augmentation. Sometimes explicitly casting req or using a generic Request<P, ResBody, ReqBody, ReqQuery> with your schema's inferred type can resolve this, though zod-express aims to handle this automatically.
breaking As a relatively new package (v0.0.8) forked from another project, the API surface may still be subject to frequent changes and potential breaking modifications between minor or even patch versions. Exercise caution when upgrading.
fix Review release notes carefully for any breaking changes before upgrading. Pin exact versions (`^0.0.x` or `0.0.x`) to prevent unexpected breakage.
gotcha The validation middlewares (`validateRequest`, `validateRequestBody`, etc.) will throw a `ZodError` if validation fails. Without an Express error-handling middleware, this can lead to unhandled promise rejections or generic server errors.
fix Implement a custom error-handling middleware in Express (e.g., `app.use((err, req, res, next) => { if (err instanceof z.ZodError) { return res.status(400).json({ errors: err.errors }); } next(err); });`) to gracefully handle validation failures.
gotcha The `processRequest` (and related `processRequestBody`, etc.) functions will apply Zod transformations (`.transform()`) to the request data. This means `req.body`, `req.query`, or `req.params` might be modified, which can be unexpected if you only intended to validate.
fix Use `validateRequest` for validation-only scenarios where request data should remain untouched. Only use `processRequest` when you explicitly intend to modify the request data based on Zod transformations.
npm install zod-express
yarn add zod-express
pnpm add zod-express

Demonstrates defining an Express route with `validateRequest` middleware to validate URL parameters, request body, and query string against Zod schemas, including basic error handling.

import express from 'express';
import { validateRequest } from 'zod-express';
import { z } from 'zod';

// Create an express app
const app = express();
app.use(express.json()); // Essential for parsing JSON bodies

// Define an endpoint using express, zod and zod-express
app.get("/:urlParameter/", validateRequest({
    params: z.object({
      urlParameter: z.string(),
    }),
    body: z.object({
      bodyKey: z.number(),
    }),
    query: z.object({
      queryKey: z.string().length(64),
    }),
  }), (req, res) => {
    // req.params, req.body and req.query are now strictly-typed and confirm to the zod schema's above.
    // req.params has type { urlParameter: string };
    // req.body has type { bodyKey: number };
    // req.query has type { queryKey: string };
    return res.json({message: "Validation for params, body and query passed", data: { params: req.params, body: req.body, query: req.query }});  
  }
);

// Add a basic error handling middleware to catch ZodErrors
app.use((err, req, res, next) => {
  if (err instanceof z.ZodError) {
    return res.status(400).json({ error: 'Validation failed', details: err.errors });
  }
  next(err);
});

// Start the express app on port 8080
const PORT = 8080;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});