Zod Express Middleware
`zod-express-middleware` is an Express.js middleware library designed to enforce type safety and validate incoming request data (body, query, and parameters) using Zod schemas. Currently at version 1.4.0, it provides functions like `validateRequest` to check if an incoming request conforms to predefined Zod schemas without modifying the request object. For scenarios requiring data transformation or stripping unknown keys, it offers `processRequest` and its specific variants (`processRequestBody`, `processRequestQuery`, `processRequestParams`), which leverage Zod's `.transform` and `.refine` methods. The package maintains a steady release cadence, integrating smoothly with `express` and `zod` as peer dependencies. Its primary differentiator is the direct integration of Zod's powerful, inferential schema validation capabilities into the Express middleware pipeline, providing a robust solution for ensuring API contract adherence and improving developer experience through strong typing.
Common errors
-
Cannot find module 'express' or 'zod'
cause One of the peer dependencies (express or zod) is not installed or incorrectly configured in the project.fixEnsure `express` and `zod` are explicitly installed in your project: `npm install express zod`. -
Property 'body' does not exist on type 'Request<ParamsDictionary, any, any, QueryString.ParsedQs, Record<string, any>>'.
cause TypeScript cannot infer the types of `req.body`, `req.query`, or `req.params` because `@types/express` is missing or the validation middleware is not correctly applied in a TypeScript environment.fixInstall `@types/express`: `npm install --save-dev @types/express`. Ensure your route handler is defined immediately after `validateRequest` so TypeScript can infer the request object's updated type. -
ZodError: Validation failed
cause The incoming request data (body, query, or params) does not conform to the defined Zod schema.fixReview the `err.errors` array within the `ZodError` instance to understand which fields failed validation and why. Adjust the incoming request data or refine your Zod schema to match expected inputs. Implement a custom error handler to return specific validation messages to the client. -
TypeError: Cannot read properties of undefined (reading 'someProperty')
cause Attempting to access properties on `req.body`, `req.query`, or `req.params` before `express.json()`, `express.urlencoded()`, or `express.query()` middleware has parsed the respective part of the request, or when a property is missing and not explicitly `optional()` in the schema.fixEnsure that appropriate Express body-parsing middleware (`app.use(express.json())`, `app.use(express.urlencoded({ extended: true }))`) is applied *before* the `zod-express-middleware` in your Express app. Also, mark optional fields in your Zod schema with `.optional()` or `.nullable()`.
Warnings
- gotcha This package relies on `express`, `zod`, and `@types/express` as peer dependencies. Failure to install these or installing incompatible versions will lead to runtime errors or TypeScript compilation issues.
- gotcha `validateRequest` and its variants only perform validation and do not modify the `req.body`, `req.query`, or `req.params` objects to reflect parsed or transformed values. If you need to apply Zod's `.transform()` or `.refine()` methods that alter the data, you must use the `processRequest` functions (e.g., `processRequest`, `processRequestBody`).
- gotcha The default error messages provided by Zod for validation failures might be too technical for end-users. You'll likely need to implement custom error handling to present more user-friendly messages.
- deprecated As of June 17, 2021, the repository maintainer noted that the project is not likely to receive significant updates in the future, suggesting alternatives like `express-zod-api` or `express-zod-safe` for new projects seeking more active development and features.
- breaking Recent Zod versions (e.g., 3.25.76 and higher) can bundle Zod 4 code, which uses TypeScript 5+ syntax. This can cause build failures for projects still on TypeScript 4.x, even when `zod-express-middleware` itself has not updated its direct dependencies, due to `zod` being a peer dependency.
Install
-
npm install zod-express-middleware -
yarn add zod-express-middleware -
pnpm add zod-express-middleware
Imports
- validateRequest
const { validateRequest } = require('zod-express-middleware');import { validateRequest } from 'zod-express-middleware'; - processRequest
import { validateRequest } from 'zod-express-middleware'; // Used for transformationsimport { processRequest } from 'zod-express-middleware'; - z
import { z } from 'zod'; - TypedRequestBody
import { TypedRequestBody } from 'zod-express-middleware';
Quickstart
import express from 'express';
import { validateRequest } from 'zod-express-middleware';
import { z } from 'zod';
// Create an express app
const app = express();
// Add express.json() middleware to parse JSON bodies
app.use(express.json());
// Define an endpoint using express, zod and zod-express-middleware
app.post("/:urlParameter/", validateRequest({
params: z.object({
urlParameter: z.string().uuid("Invalid URL parameter format"),
}),
body: z.object({
bodyKey: z.number().int().positive("bodyKey must be a positive integer"),
optionalField: z.string().optional()
}),
query: z.object({
queryKey: z.string().length(64, "queryKey must be 64 characters long"),
}),
}), (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; optionalField?: string };
// req.query has type { queryKey: string };
console.log('Validated Params:', req.params);
console.log('Validated Body:', req.body);
console.log('Validated Query:', req.query);
return res.json({message: "Validation for params, body and query passed", data: { params: req.params, body: req.body, query: req.query }});
}
);
app.get('/', (req, res) => {
res.send('Welcome to the Zod Express Middleware example!');
});
// Error handling middleware for ZodErrors
app.use((err, req, res, next) => {
if (err instanceof z.ZodError) {
return res.status(400).json({
status: 'error',
message: 'Validation failed.',
errors: err.errors.map(e => ({ path: e.path.join('.'), message: e.message }))
});
}
next(err);
});
// Start the express app on port 8080
const PORT = process.env.PORT ?? 8080;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log('Try POST to /<uuid-param>?queryKey=... (64 chars) with JSON body { "bodyKey": 123 }');
});