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 manager | One-off run | Install as a dev dependency |
|---|---|---|
| npm | npx routeman-cli | npm i -D routeman-cli |
| pnpm | pnpm dlx routeman-cli | pnpm add -D routeman-cli |
| yarn | yarn dlx routeman-cli | yarn add -D routeman-cli |
| bun | bunx routeman-cli | bun add -d routeman-cli |
| deno | deno 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
| Framework | Guide |
|---|---|
| Express 4 / 5 | Express to Postman |
| NestJS | NestJS to Postman |
| Fastify 4 / 5 | Fastify 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:http | What 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.
| Option | Meaning |
|---|---|
-C, --project DIR | Project folder (default: current folder) |
-f, --framework NAME | Force a framework: express, nest, fastify, next, nuxt, hono, koa, elysia, hapi, adonis, sveltekit… (default: detect) |
-a, --entry FILE | Entry file, e.g. src/server.ts (repeatable; detected from package.json scripts and main) |
-n, --name NAME | Collection name |
-o, --output DIR | Output folder (default postman) |
-b, --base-url URL | Base URL of the local environment |
-e, --env NAME=URL | Add an environment, e.g. -e production=https://api.example.com (repeatable) |
-x, --exclude REGEX | Leave out matching paths, e.g. -x '^/internal/' (repeatable) |
--auth TYPE | Force none, bearer, token, basic, apikey, header or session |
--login PATH | The POST route whose response contains the token |
--openapi SRC | Build 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) |
--stdout | Print the collection instead of writing files |
-q, --quiet | Only 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-urlencodedormultipart/form-data(file uploads become Postman file pickers). Example values pass validation: they respect enums, min/max, lengths, regex patterns and field names (emailgetsuser@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),PORTin.env). - Tests: every request checks that the response is not a 5xx.
- Descriptions: names and descriptions from JSDoc on handler functions and methods,
@ApiOperation, Fastifyschema.summaryor Elysiadetail, 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.
| Detected | Postman setup |
|---|---|
| Bearer / JWT | Bearer token {{access_token}} |
Token prefix | Authorization: Token {{access_token}} |
| API key or custom token header | The header, e.g. x-api-key |
| Basic | Basic auth with {{username}} / {{password}} |
| Session | Cookies |
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.