postman-collection-generator

Generates Postman collection JSON files from Express, Next.js, Fastify, Hono, or other API routes. Scans route definitions, extracts endpoints, methods,…

INSTALLATION
npx skills add https://github.com/patricio0312rev/skills --skill postman-collection-generator
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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<string, unknown>;

  headers?: Record<string, string>;

}

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<NextApiRoute[]> {

  const routes: NextApiRoute[] = [];

  const routeFiles = await glob(`${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) &#x26;&#x26; 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<string, RouteInfo[]> {

  const groups: Record<string, RouteInfo[]> = {};

  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 <type>", "Framework type", "express")

  .option("-s, --source <path>", "Source directory", "./src")

  .option("-o, --output <path>", "Output file", "./postman-collection.json")

  .option("-n, --name <name>", "Collection name", "API Collection")

  .option("-b, --base-url <url>", "Base URL", "http://localhost:3000")

  .option("-a, --auth <type>", "Auth type (bearer|basic|apikey)")

  .parse();

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
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card