npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

express-zod-routes

v1.1.0

Published

Express middleware to validate body, query, and params with Zod — typed req.validated and consistent 400 errors

Readme

express-zod-routes

CI npm License: MIT

Express middleware that validates body, query, and params with Zod. On success, parsed values land on req.validated. On failure, the client gets a consistent JSON 400 (or your chosen status) without calling next.

Built for MERN / TypeScript stacks: one source of truth for validation, no duplicate DTO logic.

Install

npm install express-zod-routes

Peer dependencies:

  • express ^4.18.0 || ^5.0.0
  • zod ^3.22.0

CLI — scaffold a route module

Generate a Router with validate({ body }) already wired (Zod + express-zod-routes):

npx express-zod-routes add-route users
npx express-zod-routes add-route products --out-dir src/routes
npx express-zod-routes add-route order-item --mount /api/order-items --force

Creates e.g. routes/users.routes.ts. Mount in your app:

import { usersRouter } from './routes/users.routes.js';
app.use('/users', usersRouter);

Run npx express-zod-routes --help for all options.

Quick start

import express from 'express';
import { z } from 'zod';
import { validate } from 'express-zod-routes';

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

app.post(
  '/users',
  validate({
    body: z.object({
      name: z.string().min(1),
      email: z.string().email(),
    }),
  }),
  (req, res) => {
    // req.validated.body is parsed & typed with InferValidated (see below)
    res.status(201).json(req.validated!.body);
  }
);

API

validate(schemas) · createValidator(schemas)

Same function; createValidator is an alias.

| Option | Type | Description | | -------------- | --------- | ------------------------------------------------- | | body | ZodType | Validates req.body (use after express.json()) | | query | ZodType | Validates req.query (strings → use z.coerce) | | params | ZodType | Validates req.params | | statusCode | number | Default 400 | | errorMessage | string | Default "Validation failed" |

At least one of body, query, or params is required.

req.validated

After the middleware runs successfully, req.validated contains only the keys you validated (merged if you stack multiple validators on the same route — usually one per route is enough).

Importing the package augments Express’s Request so validated is recognized by TypeScript.

Typing handlers with InferValidated

import type { Request } from 'express';
import { validate, type InferValidated } from 'express-zod-routes';

const schemas = {
  body: z.object({ email: z.string().email() }),
} as const;

app.post(
  '/',
  validate(schemas),
  (req: Request & { validated: InferValidated<typeof schemas> }, res) => {
    req.validated.body.email;
    res.sendStatus(204);
  }
);

Error response shape

{
  "error": "Validation failed",
  "issues": [{ "path": "email", "message": "Invalid email", "code": "invalid_string" }]
}

mapZodErrorToResponse(err, message?)

Use in your own error middleware if you reuse the same JSON shape.

Query & params tips

  • Query values are strings (or arrays) from Express. Use z.coerce.number(), z.enum(), etc.
  • Params are strings; use z.string().uuid(), regex, or transforms as needed.

Comparison

| Approach | express-zod-routes | | --------------------- | ------------------------ | | Manual if + res 400 | Centralized Zod + format | | Only body validation | body + query + params |

Other great options exist (express-zod-api, custom Zod wrappers). This package stays small: one middleware, req.validated, stable errors.

Example project

See examples/basic-express.

Maintainers

Internal docs (publish to npm with provenance, historical plan): docs/README.md.

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md. Prefer private advisories over public issues for vulnerabilities.

Maintainers: releases with npm provenance are documented in docs/PUBLISH_NPM.md.

License

MIT © see LICENSE.