Postman Collection Generator
Generate importable Postman collections from your API codebase automatically.
Core Workflow Scan routes: Find all API route definitions in the codebase Extract metadata: Methods, paths, params, request bodies, headers Organize endpoints: Group by resource or folder structure Generate collection: Create Postman Collection v2.1 JSON Add examples: Include request/response examples Configure variables: Environment variables for base URL, auth tokens Supported Frameworks Framework Route Pattern Detection Express app.get(), router.post() Method chaining on app/router Next.js app/api/**/route.ts File-based routing Fastify fastify.get(), route schema Method + schema decorators Hono app.get(), app.post() Similar to Express NestJS @Get(), @Post() decorators Decorator-based Koa router.get(), router.post() Koa-router patterns Postman Collection v2.1 Schema { "info": { "name": "API Collection", "description": "Auto-generated from codebase", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [], "variable": [], "auth": {} }
Express Route Scanner // scripts/generate-postman.ts import * as fs from "fs"; import * as path from "path"; import { parse } from "@babel/parser"; import traverse from "@babel/traverse";
interface RouteInfo {
method: string;
path: string;
name: string;
description?: string;
params?: ParamInfo[];
body?: Record
interface ParamInfo { name: string; type: "path" | "query"; description?: string; example?: string; }
function scanExpressRoutes(filePath: string): RouteInfo[] { const routes: RouteInfo[] = []; const code = fs.readFileSync(filePath, "utf-8");
const ast = parse(code, { sourceType: "module", plugins: ["typescript"], });
traverse(ast, { CallExpression(nodePath) { const callee = nodePath.node.callee;
if (callee.type === "MemberExpression") {
const method = callee.property.name;
const httpMethods = ["get", "post", "put", "patch", "delete"];
if (httpMethods.includes(method)) {
const args = nodePath.node.arguments;
if (args[0]?.type === "StringLiteral") {
const routePath = args[0].value;
routes.push({
method: method.toUpperCase(),
path: routePath,
name: generateRouteName(method, routePath),
params: extractParams(routePath),
});
}
}
}
},
});
return routes; }
function extractParams(routePath: string): ParamInfo[] { const params: ParamInfo[] = []; const pathParamRegex = /:(\w+)/g; let match;
while ((match = pathParamRegex.exec(routePath)) !== null) {
params.push({
name: match[1],
type: "path",
example: {{${match[1]}}},
});
}
return params; }
function generateRouteName(method: string, path: string): string {
const cleanPath = path.replace(/[/:]/g, " ").trim();
return ${method.toUpperCase()} ${cleanPath};
}
Next.js App Router Scanner // scripts/scan-nextjs-routes.ts import * as fs from "fs"; import * as path from "path"; import { glob } from "glob";
interface NextApiRoute { method: string; path: string; filePath: string; }
async function scanNextJsRoutes(appDir: string): Promise${appDir}/**/route.{ts,js});
for (const file of routeFiles) { const content = fs.readFileSync(file, "utf-8"); const relativePath = path.relative(appDir, path.dirname(file)); const apiPath = "/" + relativePath.replace(/\/g, "/");
// Detect exported HTTP methods
const methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
for (const method of methods) {
if (
content.includes(`export async function ${method}`) ||
content.includes(`export function ${method}`) ||
content.includes(`export const ${method}`)
) {
routes.push({
method,
path: convertNextPathToPostman(apiPath),
filePath: file,
});
}
}
}
return routes; }
function convertNextPathToPostman(nextPath: string): string { // Convert [param] to :param return nextPath .replace(/[...(\w+)]/g, ":$1") // [...slug] -> :slug .replace(/[(\w+)]/g, ":$1"); // [id] -> :id }
Fastify Route Scanner // scripts/scan-fastify-routes.ts interface FastifyRoute { method: string; path: string; schema?: { body?: object; querystring?: object; params?: object; response?: object; }; }
function scanFastifyRoutes(filePath: string): FastifyRoute[] { const routes: FastifyRoute[] = []; const code = fs.readFileSync(filePath, "utf-8");
// Match fastify.get('/path', { schema: ... }, handler)
const routeRegex =
/fastify.(get|post|put|patch|delete)\s(\s['"]([^'"]+)['"`]\s,\s({[\s\S]?})\s,/g;
let match; while ((match = routeRegex.exec(code)) !== null) { const [, method, path, optionsStr] = match;
routes.push({
method: method.toUpperCase(),
path,
// Parse schema from options if available
});
}
return routes; }
Collection Generator // scripts/generate-collection.ts interface PostmanCollection { info: { name: string; description: string; schema: string; }; item: PostmanItem[]; variable: PostmanVariable[]; auth?: PostmanAuth; }
interface PostmanItem { name: string; request: { method: string; header: PostmanHeader[]; url: PostmanUrl; body?: PostmanBody; description?: string; }; response?: PostmanResponse[]; }
interface PostmanUrl { raw: string; host: string[]; path: string[]; query?: PostmanQuery[]; variable?: PostmanPathVariable[]; }
interface PostmanVariable { key: string; value: string; type: string; }
function generatePostmanCollection( routes: RouteInfo[], options: { name: string; baseUrl: string; description?: string; auth?: "bearer" | "basic" | "apikey"; } ): PostmanCollection { const collection: PostmanCollection = { info: { name: options.name, description: options.description || "Auto-generated API collection", schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", }, item: [], variable: [ { key: "baseUrl", value: options.baseUrl, type: "string" }, { key: "authToken", value: "", type: "string" }, ], };
// Add auth configuration if (options.auth === "bearer") { collection.auth = { type: "bearer", bearer: [{ key: "token", value: "{{authToken}}", type: "string" }], }; }
// Group routes by resource const groupedRoutes = groupRoutesByResource(routes);
for (const [resource, resourceRoutes] of Object.entries(groupedRoutes)) { const folder: PostmanItem = { name: resource, item: resourceRoutes.map((route) => createPostmanRequest(route)), }; collection.item.push(folder); }
return collection; }
function createPostmanRequest(route: RouteInfo): PostmanItem { const pathSegments = route.path.split("/").filter(Boolean);
const item: PostmanItem = {
name: route.name,
request: {
method: route.method,
header: [
{ key: "Content-Type", value: "application/json", type: "text" },
],
url: {
raw: {{baseUrl}}${route.path},
host: ["{{baseUrl}}"],
path: pathSegments,
variable: route.params
?.filter((p) => p.type === "path")
.map((p) => ({
key: p.name,
value: p.example || "",
description: p.description,
})),
},
description: route.description,
},
};
// Add request body for POST/PUT/PATCH if (["POST", "PUT", "PATCH"].includes(route.method) && route.body) { item.request.body = { mode: "raw", raw: JSON.stringify(route.body, null, 2), options: { raw: { language: "json" } }, }; }
return item; }
function groupRoutesByResource(
routes: RouteInfo[]
): Record
for (const route of routes) { // Extract resource from path (e.g., /api/users/:id -> users) const parts = route.path.split("/").filter(Boolean); const resource = parts[1] || parts[0] || "root";
if (!groups[resource]) {
groups[resource] = [];
}
groups[resource].push(route);
}
return groups; }
CLI Script
!/usr/bin/env node
// scripts/postman-gen.ts import * as fs from "fs"; import * as path from "path"; import { program } from "commander";
program
.name("postman-gen")
.description("Generate Postman collection from API routes")
.option("-f, --framework
const options = program.opts();
async function main() { let routes: RouteInfo[] = [];
switch (options.framework) {
case "express":
routes = await scanExpressProject(options.source);
break;
case "nextjs":
routes = await scanNextJsRoutes(path.join(options.source, "app/api"));
break;
case "fastify":
routes = await scanFastifyProject(options.source);
break;
default:
console.error(Unsupported framework: ${options.framework});
process.exit(1);
}
const collection = generatePostmanCollection(routes, { name: options.name, baseUrl: options.baseUrl, auth: options.auth, });
fs.writeFileSync(options.output, JSON.stringify(collection, null, 2));
console.log(Generated ${options.output} with ${routes.length} endpoints);
}
main();
Environment Template { "name": "Development", "values": [ { "key": "baseUrl", "value": "http://localhost:3000/api", "enabled": true }, { "key": "authToken", "value": "", "enabled": true, "type": "secret" }, { "key": "userId", "value": "1", "enabled": true } ] }
Example Output { "info": { "name": "My API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "Users", "item": [ { "name": "GET users", "request": { "method": "GET", "url": { "raw": "{{baseUrl}}/users", "host": ["{{baseUrl}}"], "path": ["users"], "query": [ { "key": "page", "value": "1" }, { "key": "limit", "value": "10" } ] } } }, { "name": "GET user by ID", "request": { "method": "GET", "url": { "raw": "{{baseUrl}}/users/:id", "host": ["{{baseUrl}}"], "path": ["users", ":id"], "variable": [{ "key": "id", "value": "{{userId}}" }] } } }, { "name": "POST create user", "request": { "method": "POST", "header": [{ "key": "Content-Type", "value": "application/json" }], "body": { "mode": "raw", "raw": "{\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\"\n}" }, "url": { "raw": "{{baseUrl}}/users", "host": ["{{baseUrl}}"], "path": ["users"] } } } ] } ], "variable": [ { "key": "baseUrl", "value": "http://localhost:3000/api" }, { "key": "authToken", "value": "" } ] }
Best Practices Use variables: {{baseUrl}}, {{authToken}} for flexibility Group endpoints: Organize by resource/feature folders Add descriptions: Document each endpoint's purpose Include examples: Pre-fill request bodies with realistic data Set up auth: Configure collection-level authentication Add tests: Include basic response validation scripts Version control: Commit collection JSON to repository CI integration: Auto-generate on route changes Output Checklist All routes scanned from codebase Endpoints grouped by resource Path parameters extracted Request bodies included for POST/PUT/PATCH Environment variables configured Authentication setup (if applicable) Collection exported as v2.1 JSON Environment template created