AnnouncementNode.jsTypeScriptPostman

routeman for Node.js: Postman collections from Express, NestJS, Fastify and Next.js

routeman is now on npm as routeman-cli. Run npx routeman-cli in an Express, NestJS, Fastify, Next.js, Hono or Koa project and get a complete Postman collection, without running your code.

Yesterday we released routeman for Python. Today routeman-cli 0.1.0 is on npm. It brings the same idea to Node.js, Bun and Deno: point it at your API's source code and get a Postman collection you can send requests with straight away.

$ cd your-api
$ npx routeman-cli

It supports Express, Fastify, NestJS, Koa, Hono, Elysia, Hapi, AdonisJS, Next.js, Nuxt, SvelteKit, Astro, Remix and about a dozen smaller frameworks. You don't need OpenAPI, Swagger decorators or a running server.

It never runs your code

This is the biggest difference from the Python edition. Python routeman imports your app, because Django and Flask build their route tables at import time. That approach fails in Node.js: starting an Express or NestJS app usually means connecting to a database, reading secrets and opening ports.

So routeman-cli reads the source instead. It parses every file with a vendored copy of the Babel parser (JavaScript, TypeScript, JSX, decorators). Then it runs a small abstract interpreter that does what the framework would do at start-up: it follows imports, app.use('/api', router) mounts, register(plugin, { prefix }), @Controller paths, global prefixes and versioning, and records every route that would be registered. It evaluates string constants, process.env values from .env, function calls, classes and loops over static arrays. It never touches the network or a database, and a hard step budget keeps it fast: about 3,000 routes in 0.8 seconds.

In practice that means it works on a fresh git clone, without npm install, a tsc build or a .env full of real secrets.

What it found in four real projects

We wrote small but realistic APIs in the four most popular frameworks and ran routeman-cli 0.1.0 on each. Every line below is real output.

# Express 5: three routers under /api/v1, JWT middleware, zod, multer
✓ express: 12 requests (5 POST, 4 GET, 1 PUT, 1 DELETE, 1 PATCH)
✓ auth: bearer (login: POST /api/v1/auth/login)

# NestJS: global prefix, URI versioning, APP_GUARD + @Public(), class-validator DTOs
✓ nest: 8 requests (3 POST, 3 GET, 1 PATCH, 1 DELETE)
✓ auth: bearer (login: POST /api/v1/auth/login)

# Fastify 5: prefixed plugins, TypeBox schemas, @fastify/jwt in an onRequest hook
✓ fastify: 6 requests (2 POST, 2 GET, 1 PUT, 1 DELETE)
✓ auth: bearer (login: POST /v1/auth/token)

# Next.js 15: App Router route handlers, a route group, zod, formData()
✓ next: 7 requests (4 POST, 2 GET, 1 DELETE)
✓ auth: bearer (login: POST /api/auth/login)

In each project the paths were complete, with prefixes, the NestJS api/v1 and the dropped (marketing) route group all handled. Public routes were set to No Auth, and the login request was found and scripted to store its token. Every body passed its validation. A zod .regex(/^[A-Z]{3}-\d{4}$/) SKU got AAA-1111, a class-validator @IsEnum got a real enum value, and a TypeBox union of literals got "yellow". The framework guides walk through each project: Express, NestJS, Fastify and Next.js.

Where request bodies come from

routeman-cli looks for the most precise source first and falls back step by step:

  1. Validation schemas: zod (v3 and v4), Joi and celebrate, yup, TypeBox and Elysia t, valibot, VineJS, express-validator, JSON Schema and class-validator DTOs, including NestJS mapped types.
  2. TypeScript types: Request<{}, {}, CreateUserBody>, req.body as Dto.
  3. ORM models: Mongoose, Prisma, Sequelize and TypeORM, when the handler passes the body straight to the database.
  4. The handler code: const { email, password } = req.body, c.req.json(), await request.json(), readBody(event). These requests say in their description that the field list was inferred.

Example values are chosen to pass validation. They respect enums, min/max, lengths, regex patterns and formats, and they use field names, so email gets user@example.com.

Same output as the Python edition

Whichever language your API is written in, you get the same kind of collection:

  • Postman Collection v2.1 with one folder per resource.
  • Collection-level auth (Bearer/JWT, token, API key, custom header, Basic or session), with public routes set to No Auth.
  • A login request whose test script saves the token automatically.
  • One environment per server. The local port is read from app.listen() or .env.
  • A 5xx check on every request, so the collection works as a Newman smoke test.
  • Stable ids, so a re-import replaces the old collection instead of duplicating it.

Install it any way you like

npx routeman-cli                     # npm
pnpm dlx routeman-cli                # pnpm
yarn dlx routeman-cli                # yarn
bunx routeman-cli                    # bun
deno run -A npm:routeman-cli         # deno

npm i -D routeman-cli                # or keep it as a dev dependency: the command is `routeman`

It has zero dependencies and needs Node.js 18.3 or newer. There's also an OpenAPI escape hatch, --openapi spec.json, for frameworks it doesn't support yet, and a programmatic API (import { generate, write } from 'routeman-cli') with TypeScript types.

What it can't do

Static analysis has one honest limit. If a route's path is built at runtime from data routeman-cli can't see, such as paths loaded from a database or a remote config, it can't find that route. routeman routes prints a warning when it notices, and --entry helps when the entry file isn't obvious from package.json.

Try it on your project, and if something is missing, send us the output of npx routeman-cli routes at routeman@swastik.ai or open an issue on GitHub. The Node.js docs cover every option.

Try routeman on your project

$ cd your-api
$ npx routeman-cli

Read the docs → Ask a question