openapi-generator

Generate OpenAPI 3.0/3.1 specifications from your API codebase automatically.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "openapi-generator" with this command: npx skills add patricio0312rev/skills/patricio0312rev-skills-openapi-generator

OpenAPI Generator

Generate OpenAPI 3.0/3.1 specifications from your API codebase automatically.

Core Workflow

  • Scan routes: Find all API route definitions

  • Extract schemas: Types, request/response bodies, params

  • Build paths: Convert routes to OpenAPI path objects

  • Generate schemas: Create component schemas from types

  • Add documentation: Descriptions, examples, tags

  • Export spec: YAML or JSON format

OpenAPI 3.1 Base Template

openapi: 3.1.0 info: title: API Title version: 1.0.0 description: API description contact: email: api@example.com license: name: MIT url: https://opensource.org/licenses/MIT

servers:

tags:

  • name: Users description: User management endpoints
  • name: Products description: Product catalog endpoints

paths: {}

components: schemas: {} securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT apiKey: type: apiKey in: header name: X-API-Key

security:

  • bearerAuth: []

TypeScript to OpenAPI Schema Converter

// scripts/type-to-schema.ts import * as ts from "typescript";

interface OpenAPISchema { type?: string; properties?: Record<string, OpenAPISchema>; required?: string[]; items?: OpenAPISchema; $ref?: string; enum?: string[]; format?: string; description?: string; example?: unknown; }

function typeToOpenAPISchema( checker: ts.TypeChecker, type: ts.Type ): OpenAPISchema { // Handle primitives if (type.flags & ts.TypeFlags.String) { return { type: "string" }; } if (type.flags & ts.TypeFlags.Number) { return { type: "number" }; } if (type.flags & ts.TypeFlags.Boolean) { return { type: "boolean" }; }

// Handle arrays if (checker.isArrayType(type)) { const elementType = (type as ts.TypeReference).typeArguments?.[0]; return { type: "array", items: elementType ? typeToOpenAPISchema(checker, elementType) : {}, }; }

// Handle object types if (type.flags & ts.TypeFlags.Object) { const properties: Record<string, OpenAPISchema> = {}; const required: string[] = [];

type.getProperties().forEach((prop) => {
  const propType = checker.getTypeOfSymbolAtLocation(
    prop,
    prop.valueDeclaration!
  );
  properties[prop.name] = typeToOpenAPISchema(checker, propType);

  // Check if required (no ? modifier)
  if (!(prop.flags &#x26; ts.SymbolFlags.Optional)) {
    required.push(prop.name);
  }
});

return {
  type: "object",
  properties,
  required: required.length > 0 ? required : undefined,
};

}

// Handle union types (enums) if (type.isUnion()) { const enumValues = type.types .filter((t) => t.isStringLiteral()) .map((t) => (t as ts.StringLiteralType).value);

if (enumValues.length > 0) {
  return { type: "string", enum: enumValues };
}

}

return {}; }

Express Route Scanner with JSDoc

// scripts/express-openapi.ts import * as fs from "fs"; import * as path from "path"; import { parse } from "@babel/parser"; import traverse from "@babel/traverse";

interface RouteMetadata { method: string; path: string; summary?: string; description?: string; tags?: string[]; requestBody?: object; responses?: Record<string, object>; parameters?: object[]; security?: object[]; }

function extractJSDocMetadata(comments: string): Partial<RouteMetadata> { const metadata: Partial<RouteMetadata> = {};

// @summary const summaryMatch = comments.match(/@summary\s+(.+)/); if (summaryMatch) metadata.summary = summaryMatch[1].trim();

// @description const descMatch = comments.match(/@description\s+(.+)/); if (descMatch) metadata.description = descMatch[1].trim();

// @tags const tagsMatch = comments.match(/@tags\s+(.+)/); if (tagsMatch) metadata.tags = tagsMatch[1].split(",").map((t) => t.trim());

return metadata; }

function scanExpressWithOpenAPI(sourceDir: string): RouteMetadata[] { const routes: RouteMetadata[] = [];

// Implementation: traverse files and extract routes with JSDoc comments // Similar to postman generator but with OpenAPI-specific metadata

return routes; }

OpenAPI Path Generator

// scripts/generate-openapi.ts import * as yaml from "js-yaml";

interface OpenAPISpec { openapi: string; info: object; servers: object[]; paths: Record<string, object>; components: { schemas: Record<string, object>; securitySchemes?: object; }; tags?: object[]; security?: object[]; }

function generateOpenAPISpec( routes: RouteMetadata[], options: { title: string; version: string; description?: string; servers: { url: string; description: string }[]; } ): OpenAPISpec { const spec: OpenAPISpec = { openapi: "3.1.0", info: { title: options.title, version: options.version, description: options.description, }, servers: options.servers, paths: {}, components: { schemas: {}, securitySchemes: { bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT", }, }, }, tags: [], };

// Collect unique tags const tagSet = new Set<string>();

// Generate paths for (const route of routes) { const openAPIPath = route.path.replace(/:(\w+)/g, "{$1}");

if (!spec.paths[openAPIPath]) {
  spec.paths[openAPIPath] = {};
}

spec.paths[openAPIPath][route.method.toLowerCase()] = {
  summary: route.summary || `${route.method} ${route.path}`,
  description: route.description,
  tags: route.tags || [extractResourceTag(route.path)],
  parameters: generateParameters(route),
  requestBody: route.requestBody,
  responses: route.responses || generateDefaultResponses(route.method),
  security: route.security,
};

// Collect tags
(route.tags || [extractResourceTag(route.path)]).forEach((t) =>
  tagSet.add(t)
);

}

// Add tags to spec spec.tags = Array.from(tagSet).map((name) => ({ name }));

return spec; }

function generateParameters(route: RouteMetadata): object[] { const params: object[] = [];

// Extract path parameters const pathParamRegex = /:(\w+)/g; let match;

while ((match = pathParamRegex.exec(route.path)) !== null) { params.push({ name: match[1], in: "path", required: true, schema: { type: "string" }, description: ${match[1]} parameter, }); }

return params; }

function generateDefaultResponses(method: string): object { const responses: Record<string, object> = { "200": { description: "Successful response", content: { "application/json": { schema: { type: "object" }, }, }, }, "400": { description: "Bad request", content: { "application/json": { schema: { $ref: "#/components/schemas/Error" }, }, }, }, "401": { description: "Unauthorized", }, "404": { description: "Not found", }, "500": { description: "Internal server error", }, };

if (method === "POST") { responses["201"] = { description: "Created successfully", content: { "application/json": { schema: { type: "object" }, }, }, }; }

if (method === "DELETE") { responses["204"] = { description: "Deleted successfully", }; }

return responses; }

function extractResourceTag(path: string): string { const parts = path.split("/").filter(Boolean); return parts[0] || "default"; }

Common Schema Components

components: schemas: Error: type: object required: - code - message properties: code: type: string example: "VALIDATION_ERROR" message: type: string example: "Invalid request data" details: type: object additionalProperties: type: array items: type: string

Pagination:
  type: object
  properties:
    page:
      type: integer
      minimum: 1
      example: 1
    limit:
      type: integer
      minimum: 1
      maximum: 100
      example: 10
    total:
      type: integer
      example: 156
    total_pages:
      type: integer
      example: 16

PaginatedResponse:
  type: object
  properties:
    success:
      type: boolean
      example: true
    data:
      type: array
      items: {}
    meta:
      $ref: "#/components/schemas/Pagination"

User:
  type: object
  required:
    - id
    - email
    - name
  properties:
    id:
      type: string
      format: uuid
      example: "123e4567-e89b-12d3-a456-426614174000"
    email:
      type: string
      format: email
      example: "user@example.com"
    name:
      type: string
      example: "John Doe"
    created_at:
      type: string
      format: date-time
      example: "2024-01-15T10:30:00Z"

CreateUserRequest:
  type: object
  required:
    - email
    - name
    - password
  properties:
    email:
      type: string
      format: email
    name:
      type: string
      minLength: 2
      maxLength: 100
    password:
      type: string
      format: password
      minLength: 8

Fastify Integration

// Fastify with @fastify/swagger import Fastify from "fastify"; import swagger from "@fastify/swagger"; import swaggerUi from "@fastify/swagger-ui";

const fastify = Fastify({ logger: true });

await fastify.register(swagger, { openapi: { info: { title: "My API", version: "1.0.0", }, servers: [{ url: "http://localhost:3000" }], }, });

await fastify.register(swaggerUi, { routePrefix: "/docs", });

// Routes with schema fastify.get( "/users/:id", { schema: { params: { type: "object", properties: { id: { type: "string", format: "uuid" }, }, required: ["id"], }, response: { 200: { type: "object", properties: { id: { type: "string" }, name: { type: "string" }, email: { type: "string" }, }, }, }, }, }, async (request, reply) => { // Handler } );

NestJS Integration

// NestJS with @nestjs/swagger import { Controller, Get, Post, Body, Param } from "@nestjs/common"; import { ApiTags, ApiOperation, ApiResponse, ApiBody } from "@nestjs/swagger";

@ApiTags("users") @Controller("users") export class UsersController { @Get() @ApiOperation({ summary: "Get all users" }) @ApiResponse({ status: 200, description: "List of users", type: [UserDto] }) findAll() { // Implementation }

@Get(":id") @ApiOperation({ summary: "Get user by ID" }) @ApiResponse({ status: 200, description: "User found", type: UserDto }) @ApiResponse({ status: 404, description: "User not found" }) findOne(@Param("id") id: string) { // Implementation }

@Post() @ApiOperation({ summary: "Create new user" }) @ApiBody({ type: CreateUserDto }) @ApiResponse({ status: 201, description: "User created", type: UserDto }) create(@Body() createUserDto: CreateUserDto) { // Implementation } }

CLI Script

#!/usr/bin/env node // scripts/openapi-gen.ts import * as fs from "fs"; import * as yaml from "js-yaml"; import { program } from "commander";

program .name("openapi-gen") .description("Generate OpenAPI specification from API routes") .option("-f, --framework <type>", "Framework (express|nextjs|fastify)", "express") .option("-s, --source <path>", "Source directory", "./src") .option("-o, --output <path>", "Output file", "./openapi.yaml") .option("-t, --title <name>", "API title", "My API") .option("-v, --version <version>", "API version", "1.0.0") .option("--json", "Output as JSON instead of YAML") .parse();

const options = program.opts();

async function main() { const routes = await scanRoutes(options.framework, options.source);

const spec = generateOpenAPISpec(routes, { title: options.title, version: options.version, servers: [ { url: "http://localhost:3000/api", description: "Development" }, ], });

const output = options.json ? JSON.stringify(spec, null, 2) : yaml.dump(spec, { lineWidth: -1 });

fs.writeFileSync(options.output, output); console.log(Generated ${options.output} with ${routes.length} endpoints); }

main();

Validation Script

// scripts/validate-openapi.ts import SwaggerParser from "@apidevtools/swagger-parser";

async function validateSpec(specPath: string): Promise<void> { try { const api = await SwaggerParser.validate(specPath); console.log(API name: ${api.info.title}, Version: ${api.info.version}); console.log("OpenAPI specification is valid!"); } catch (err) { console.error("Validation failed:", err.message); process.exit(1); } }

Best Practices

  • Use $ref: Reference shared schemas to avoid duplication

  • Add examples: Include realistic examples for all schemas

  • Document errors: Define all possible error responses

  • Use tags: Organize endpoints by resource/feature

  • Version control: Commit spec to repository

  • Validate: Run validation before publishing

  • Generate SDKs: Use openapi-generator for client SDKs

  • Serve UI: Host Swagger UI or Redoc for documentation

Output Checklist

  • All routes converted to OpenAPI paths

  • Path parameters use {param} syntax

  • Request bodies defined with schemas

  • Response schemas for all status codes

  • Common schemas in components/schemas

  • Security schemes configured

  • Tags applied to all endpoints

  • Examples included for schemas

  • Spec validates without errors

  • YAML/JSON exported successfully

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

explaining-code

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

jsdoc-typescript-docs

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

codebase-summarizer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

vscode-workspace-setup

No summary provided by upstream source.

Repository SourceNeeds Review