routeman for Node.js

Generate a Postman collection from a Node.js, Bun or Deno API: Express, Fastify, NestJS, Koa, Hono, Elysia, Hapi, Next.js, Nuxt, SvelteKit, AdonisJS and more. These docs are for routeman-cli 0.1.0.

Quick start

Run it in your project folder. You don't need to install anything, start your server or build TypeScript:

$ cd your-api
$ npx routeman-cli
✓ express: 12 requests (5 POST, 4 GET, 1 PUT, 1 DELETE, 1 PATCH)
✓ auth: bearer (login: POST /api/v1/auth/login)
✓ wrote postman/shop-api.postman_collection.json
✓ wrote postman/shop-api.local.postman_environment.json
  done in 0.08s - import the files in Postman (File → Import)

Open Postman, choose File → Import and drop both files. Select the local environment, fill in username and password, and run the Login request. Every other request now sends the saved token.

Your code is never run. routeman-cli parses your files and follows imports, routers, mounts, plugins, controllers and decorators the way the framework would wire them up. It doesn't need node_modules, a database, secrets or a build step, and it can't trigger side effects. (The Python edition works differently: it imports your app read-only.)

Installation

routeman-cli needs Node.js 18.3 or newer, or Bun or Deno. It has zero dependencies (the Babel parser is vendored), so it installs in about a second.

Package managerOne-off runInstall as a dev dependency
npmnpx routeman-clinpm i -D routeman-cli
pnpmpnpm dlx routeman-clipnpm add -D routeman-cli
yarnyarn dlx routeman-cliyarn add -D routeman-cli
bunbunx routeman-clibun add -d routeman-cli
denodeno run -A npm:routeman-cli–

Once it's installed, the command is routeman, for example npx routeman routes or a "postman": "routeman" script in package.json. For one-off runs, use the package name: npx routeman-cli routes.

Supported frameworks

FrameworkGuide
Express 4 / 5Express to Postman
NestJSNestJS to Postman
Fastify 4 / 5Fastify to Postman
Next.js (App and Pages Router)Next.js to Postman
Koa, Hono, Elysia, Hapi, AdonisJS 6, Nuxt/Nitro/h3, SvelteKit, Astro, Remix, routing-controllers, tsoa, inversify-express-utils, LoopBack 4, Restify, Polka, tinyhttp, hyper-express, ultimate-express, itty-router, Feathers, Oak, Bun.serve, Deno.serve, node:httpWhat each one supports
Anything else with an OpenAPI 3 / Swagger 2 document--openapi

Commands

routeman                       # same as `routeman generate`
routeman generate --stdout     # print the collection instead of writing files
routeman routes                # list what routeman found (method, path, auth, body fields)
routeman routes --json         # machine-readable
routeman init                  # save the project details in routeman.config.json
routeman --version

generate can be shortened to gen and routes to ls. A lock icon in routes marks endpoints that need authentication:

$ npx routeman-cli routes
POST    /api/v1/auth/login                    json: email, password
GET     /api/v1/orders                      🔒
POST    /api/v1/orders                      🔒 json: productId, quantity, couponCode
POST    /api/v1/products/{productId}/image  🔒 form: image
...
12 routes, auth: bearer (login: POST /api/v1/auth/login)

routes --json gives one object per route, with the source location of the handler:

{
  "method": "POST",
  "path": "/api/v1/auth/login",
  "name": "Login",
  "auth": "none",
  "login": true,
  "body": { "mode": "json", "fields": ["email", "password"] },
  "query": [],
  "source": "src/routes/auth.js:14"
}

Options

Every option is optional. routeman-cli detects everything it can, and you only override what it gets wrong or can't guess.

OptionMeaning
-C, --project DIRProject folder (default: current folder)
-f, --framework NAMEForce a framework: express, nest, fastify, next, nuxt, hono, koa, elysia, hapi, adonis, sveltekit… (default: detect)
-a, --entry FILEEntry file, e.g. src/server.ts (repeatable; detected from package.json scripts and main)
-n, --name NAMECollection name
-o, --output DIROutput folder (default postman)
-b, --base-url URLBase URL of the local environment
-e, --env NAME=URLAdd an environment, e.g. -e production=https://api.example.com (repeatable)
-x, --exclude REGEXLeave out matching paths, e.g. -x '^/internal/' (repeatable)
--auth TYPEForce none, bearer, token, basic, apikey, header or session
--login PATHThe POST route whose response contains the token
--openapi SRCBuild from an OpenAPI/Swagger file or URL instead of the source code
--env-file FILE.env values the code reads (default: .env, then .env.example)
--stdoutPrint the collection instead of writing files
-q, --quietOnly print errors

Examples:

# staging and production environments, health checks left out
$ npx routeman-cli -e staging=https://staging.example.com \
    -e production=https://api.example.com -x '^/health'
✓ express: 11 requests (5 POST, 3 GET, 1 PUT, 1 DELETE, 1 PATCH)
✓ auth: bearer (login: POST /api/v1/auth/login)
✓ wrote postman/shop-api.postman_collection.json
✓ wrote postman/shop-api.local.postman_environment.json
✓ wrote postman/shop-api.staging.postman_environment.json
✓ wrote postman/shop-api.production.postman_environment.json

# a monorepo package with a TypeScript entry file
npx routeman-cli -C apps/api --entry src/main.ts -n "Billing API"

Configuration file (routeman.config.json)

routeman init asks a few questions and writes routeman.config.json, so the whole team and CI get the same output from a bare routeman. Pass -y to accept the detected values without being asked. You can put the same object under "routeman" in package.json instead.

{
  "$schema": "https://unpkg.com/routeman-cli/schema.json",
  "name": "Shop API",
  "framework": "express",
  "entry": ["src/server.ts"],
  "output": "postman",
  "environments": {
    "local": "http://localhost:4000",
    "production": "https://api.example.com"
  },
  "exclude": ["^/internal/"],
  "auth": { "type": "auto", "login": "/api/v1/auth/login" }
}

The $schema line gives you autocompletion in VS Code and other editors. Command-line options override the file.

What the collection contains

  • Every route across all files and mounts, with the full path (prefixes, versions, global prefix). Static files and 404 catch-alls are left out. One folder per resource.
  • Request bodies: JSON, x-www-form-urlencoded or multipart/form-data (file uploads become Postman file pickers). Example values pass validation: they respect enums, min/max, lengths, regex patterns and field names (email gets user@example.com).
  • Where bodies come from: zod (v3/v4), Joi / celebrate, yup, TypeBox / Elysia t, valibot, VineJS, express-validator, JSON Schema, class-validator DTOs, TypeScript types (Request<{}, {}, Body>, req.body as Dto), Mongoose / Prisma / Sequelize / TypeORM models when the body goes straight to the database, and finally the handler code itself (const { email, password } = req.body, c.req.json(), await request.json(), readBody(event)…). Bodies read from handler code are marked as inferred in the description.
  • Query parameters from schemas or code (req.query.page, searchParams.get('q'), c.req.query('q'), @Query('page')). Optional ones are added but switched off.
  • Path variables: {{userId}}, {{productId}}… stored in the environment and named after the resource. They're typed from the route (:id(\d+), ParseIntPipe, ParseUUIDPipe, schemas), and use Mongo ObjectIds when the project uses Mongoose.
  • Environments: one per server. The local port comes from your code (app.listen(4000), PORT in .env).
  • Tests: every request checks that the response is not a 5xx.
  • Descriptions: names and descriptions from JSDoc on handler functions and methods, @ApiOperation, Fastify schema.summary or Elysia detail, plus a table of body and query fields and the handler's source location.

The format is Postman Collection v2.1, which Postman, Newman, Insomnia, Bruno, Hoppscotch and most other API clients can import.

Authentication and the login script

Auth is detected from middleware, guards, hooks and the code itself: jwt.verify, passport.authenticate('jwt'), request.jwtVerify(), NestJS guards, reading the Authorization header, and so on.

DetectedPostman setup
Bearer / JWTBearer token {{access_token}}
Token prefixAuthorization: Token {{access_token}}
API key or custom token headerThe header, e.g. x-api-key
BasicBasic auth with {{username}} / {{password}}
SessionCookies

Public routes get No Auth. Routes with a different scheme, such as an admin router behind x-api-key, get their own auth. The login request saves access_token and refresh_token from its response, wherever they are in the JSON (token, accessToken, jwt, id_token and other common names are recognised). If the wrong route is picked, use --login /api/auth/login.

Building from OpenAPI (--openapi)

If you already publish an OpenAPI 3 or Swagger 2 document, or your framework isn't supported, build from the spec. JSON and YAML files and URLs all work:

$ npx routeman-cli --openapi openapi.yaml
✓ openapi: 2 requests (1 GET, 1 POST)
✓ auth: bearer
✓ wrote postman/pets-api.postman_collection.json
✓ wrote postman/pets-api.local.postman_environment.json

$ npx routeman-cli --openapi http://localhost:3000/docs-json     # NestJS + @nestjs/swagger

You get the same collection extras as from code: collection-level auth, environments and smoke tests.

Programmatic API

Use it from a script, a build step or a test. Types are included (index.d.ts).

import { generate, write } from 'routeman-cli';

const result = await generate({ project: '.', environments: { staging: 'https://staging.example.com' } });
console.log(result.api.routes.length, 'routes');
write(result); // or use result.collection / result.environments directly

Running in CI with Newman

Every request carries a "no server error" test, so the generated collection doubles as a smoke test of the whole API:

npm ci
npx routeman-cli -b http://localhost:3000
npm start &
npx wait-on http://localhost:3000
npx newman run postman/*.postman_collection.json \
  -e postman/*.local.postman_environment.json

See Smoke-test your API in CI with Newman for a complete pipeline. The steps are the same whatever language the API is written in.

Speed and limits

routeman-cli runs a small abstract interpreter over your files. It evaluates imports (ESM, CommonJS, tsconfig paths, package.json imports, Deno import maps), string constants, process.env values from .env, function calls, classes and loops over static arrays. It has a hard step budget, so it always finishes fast: about 3,000 routes in 0.8 seconds. It never executes anything outside your source files and never touches the network or a database.

The trade-off is that routes whose path is built at runtime from data it can't see, such as rows loaded from a database, can't be found. routeman routes prints a warning when that happens.

Troubleshooting

Routes are missing

Run npx routeman-cli routes and read the warnings. Usually the entry file has to be passed with --entry src/server.ts, or a route path is built at runtime.

The wrong framework was detected

Force it with -f nest, -f fastify and so on, then run npx routeman-cli init to save the choice.

A request body is empty or incomplete

Validate the body with a schema (zod, Joi, a DTO, TypeBox…) for the most complete result. Without one, routeman-cli reads the handler code and marks the fields as inferred.

The wrong login route was picked

Use --login /your/login/path, or set "auth": { "login": "…" } in routeman.config.json.

Still stuck? Email routeman@swastik.ai with your framework and version and the output of npx routeman-cli routes, or open an issue on GitHub.