postman-collection-generator

安装量: 134
排名: #6447

安装

npx skills add https://github.com/patricio0312rev/skills --skill postman-collection-generator

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; headers?: 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 { 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) && 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 { const groups: 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 ", "Framework type", "express") .option("-s, --source ", "Source directory", "./src") .option("-o, --output ", "Output file", "./postman-collection.json") .option("-n, --name ", "Collection name", "API Collection") .option("-b, --base-url ", "Base URL", "http://localhost:3000") .option("-a, --auth ", "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

返回排行榜