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

@thunder03/expressapidoc

v0.1.3

Published

FastAPI-style auto-generated API docs for Express, powered by Zod

Readme

expressapidoc

FastAPI-style interactive API docs for Express, generated from your Zod schemas.

Define each route's params, query and body once as Zod schemas. You get:

  • Request validation: invalid requests are rejected with a 422 before your handler runs.
  • Type coercion: z.coerce.number() turns the URL string "42" into the number 42 before your code sees it.
  • A live OpenAPI 3 spec at /openapi.json.
  • A Swagger UI page at /api-docs, with "Try it out" buttons.

You write no OpenAPI YAML, no JSDoc annotations and no Swagger setup code.

Status: v0.1, early release. Please read Limitations before using it in a real project.

Install

npm install expressapidoc express zod

express (v4) and zod (v3) are peer dependencies, so your project keeps control of their versions.

Quick start

import express from 'express';
import { z } from 'zod';
import { createDocRouter, mountDocs } from 'expressapidoc';

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

const router = createDocRouter();

// router.<method>(path, schemas, handler)
router.get('/hello', {}, (req, res) => {
  res.json({ message: 'hi' });
});

router.get(
  '/items/:id',
  {
    params: z.object({ id: z.coerce.number() }),
    query: z.object({ q: z.string().optional() }),
    summary: 'Get an item by id',
  },
  (req, res) => {
    res.json({ id: req.params.id, q: req.query.q }); // id is a real number here
  }
);

router.post(
  '/items',
  { body: z.object({ name: z.string(), price: z.number() }) },
  (req, res) => {
    res.json(req.body); // already validated
  }
);

app.use(router);
mountDocs(app);

app.listen(3000, () => console.log('Docs at http://localhost:3000/api-docs'));

Open http://localhost:3000/api-docs and every route above is listed with its parameters and request body.

API

createDocRouter()

Returns an Express router. Use it like express.Router(), with one difference: get, post, put, patch and delete take three arguments:

router.get(path, schemas, handler)

| Argument | Description | |-----------|-------------| | path | Express path, e.g. /items/:id | | schemas | { params?, query?, body?, summary? }. Every field is optional. Pass {} for a route with no validation. | | handler | A normal Express handler (req, res, next) => {} |

If a schema is provided and the incoming data does not match it, the request is answered with HTTP 422 and the Zod issues, and your handler is not called.

mountDocs(app, path = '/docs')

Adds two endpoints to your app:

  • GET /openapi.json: the OpenAPI spec, built when the request arrives, so it always reflects every route registered by then.
  • GET <docsPath>: the Swagger UI page.

Call it before or after your routes; the order doesn't matter.

generateOpenApiSpec(title?, version?)

Returns the OpenAPI document as a plain object, in case you want to write it to a file or serve it yourself.

How it works

The design follows the pipeline FastAPI uses:

| Step | FastAPI | expressapidoc | |------|---------|---------------| | 1. Capture routes | @app.get(...) decorators append to app.routes | createDocRouter() wraps router.get/post/... and pushes each route into an internal registry | | 2. Describe inputs | Python type hints + Pydantic models | Zod schemas passed explicitly (TypeScript types don't exist at runtime) | | 3. Validate | Pydantic returns 422 on mismatch | The wrapped handler calls schema.parse(...) and returns 422 on failure | | 4. Build the spec | Walks routes and builds openapi.json on first request | Converts each Zod schema with zod-to-json-schema and assembles OpenAPI 3 | | 5. Show the UI | /docs is Swagger UI pointed at /openapi.json | Same: swagger-ui-express fetches /openapi.json |

The spec is generated when /openapi.json is requested, not at startup. By then your whole app file has run and every route is registered, so mountDocs(app) works wherever you put it.

Limitations

  • Register the router directly on the app: app.use(router). Mounting under a prefix, such as app.use('/api', router), is not supported yet. The docs would show /hello while the real endpoint is /api/hello.
  • Three-argument form only. router.get('/x', handler) does not work; use router.get('/x', {}, handler).
  • Only routes created with createDocRouter() appear in the docs. Plain app.get(...) routes are not tracked.
  • Only get, post, put, patch and delete are wrapped. router.use, router.all and router.route are not.
  • Express 4 and Zod 3 only. Express 5 and Zod 4 are not supported yet.
  • ESM only. Use import, not require.
  • Response schemas are not documented. Every route shows generic 200 and 422 responses.

Roadmap

  • Support for mounting routers under a prefix
  • Response schemas
  • Optional two-argument form for routes without schemas
  • Express 5 and Zod 4 support

Development

git clone https://github.com/thunDer2203/RestApiDoc.git
cd expressapidoc
npm install
npm test        # run the test suite (Vitest + Supertest)
npm run build   # compile TypeScript to dist/

License

MIT