Files
gh-jeremylongshore-claude-c…/commands/generate-api-docs.md
2025-11-29 18:52:11 +08:00

52 KiB

description, shortcut
description shortcut
Generate comprehensive OpenAPI/Swagger documentation from existing APIs apidoc

Generate API Documentation

Automatically generate comprehensive OpenAPI 3.0 specifications with interactive documentation, automated testing, and multi-language client SDKs from your existing API codebase or by analyzing live endpoints.

When to Use This Command

Use /generate-api-docs when you need to:

  • Document existing APIs without manual specification writing
  • Generate interactive API documentation for developers
  • Create Postman/Insomnia collections from your API
  • Enable "try it out" functionality for API endpoints
  • Generate client SDKs in multiple languages
  • Maintain API documentation synchronized with code
  • Create API reference documentation for external consumers
  • Validate API contracts and schemas
  • Enable API mocking for frontend development

DON'T use this when:

  • Your API is still in early design phase (use /api-contract-generator instead)
  • You only need internal code comments (use JSDoc/docstrings)
  • Building GraphQL APIs (use /build-graphql-server with introspection)
  • APIs have no stable contract (too early in development)

Design Decisions

This command implements OpenAPI 3.0.3 specification as the primary approach because:

  • Industry standard adopted by major organizations
  • Extensive tooling ecosystem (Swagger UI, Redoc, SDKs)
  • Supports complex schemas including oneOf, allOf, discriminators
  • Native support for webhooks and callbacks
  • JSON Schema validation built-in
  • Multiple security schemes support

Alternative considered: API Blueprint

  • Markdown-based format
  • Simpler for human writing
  • Less tooling support
  • Recommended for documentation-first design

Alternative considered: RAML

  • YAML-based with reusable components
  • Good for large enterprise APIs
  • Smaller ecosystem
  • Recommended when modularity is critical

Alternative considered: AsyncAPI

  • Specialized for event-driven APIs
  • WebSocket and message queue support
  • Use alongside OpenAPI for complete coverage

Prerequisites

Before running this command:

  1. Functioning API with consistent patterns
  2. Request/response examples available
  3. Authentication mechanism implemented
  4. Error response format standardized
  5. API versioning strategy defined

Implementation Process

Step 1: API Analysis and Discovery

Scan codebase and running API to discover all endpoints, methods, and data structures.

Step 2: Schema Extraction

Extract request/response schemas from code annotations, TypeScript types, or runtime analysis.

Step 3: OpenAPI Generation

Generate complete OpenAPI 3.0 specification with all paths, schemas, and security definitions.

Step 4: Documentation Enhancement

Add descriptions, examples, and grouping tags for better organization and understanding.

Step 5: Interactive Documentation Setup

Deploy Swagger UI and Redoc for interactive API exploration and testing.

Output Format

The command generates:

  • openapi.yaml - Main OpenAPI 3.0 specification
  • openapi.json - JSON format for tooling
  • docs/ - Static HTML documentation
    • index.html - Swagger UI interface
    • redoc.html - Redoc documentation
  • collections/ - External tool formats
    • postman_collection.json - Postman import
    • insomnia_workspace.json - Insomnia import
    • bruno_collection.bru - Bruno collection
  • examples/ - Request/response examples
  • schemas/ - Extracted JSON schemas
  • README.md - Getting started guide

Code Examples

Example 1: Express.js API with Automatic Documentation Generation

// api-documentation-generator.js
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const { z } = require('zod');
const zodToJsonSchema = require('zod-to-json-schema');
const express = require('express');
const fs = require('fs').promises;
const path = require('path');
const yaml = require('js-yaml');

class APIDocumentationGenerator {
  constructor(app, config = {}) {
    this.app = app;
    this.config = {
      title: config.title || 'API Documentation',
      version: config.version || '1.0.0',
      description: config.description || 'API Documentation',
      servers: config.servers || [{ url: 'http://localhost:3000' }],
      contact: config.contact || {},
      license: config.license || { name: 'MIT' },
      security: config.security || [],
      ...config
    };

    this.paths = {};
    this.schemas = {};
    this.examples = {};
    this.tags = [];
  }

  // Analyze Express routes and generate OpenAPI paths
  analyzeRoutes() {
    const routes = [];

    // Extract all routes from Express app
    this.app._router.stack.forEach((middleware) => {
      if (middleware.route) {
        // Regular routes
        routes.push({
          path: middleware.route.path,
          methods: Object.keys(middleware.route.methods)
        });
      } else if (middleware.name === 'router') {
        // Router middleware
        middleware.handle.stack.forEach((handler) => {
          if (handler.route) {
            const basePath = middleware.regexp.source
              .replace('\\/?', '')
              .replace('(?=\\/|$)', '')
              .replace(/\\/g, '/')
              .replace('^', '');

            routes.push({
              path: basePath + handler.route.path,
              methods: Object.keys(handler.route.methods)
            });
          }
        });
      }
    });

    // Convert Express routes to OpenAPI paths
    routes.forEach(route => {
      const openApiPath = this.expressToOpenAPIPath(route.path);

      if (!this.paths[openApiPath]) {
        this.paths[openApiPath] = {};
      }

      route.methods.forEach(method => {
        this.paths[openApiPath][method] = this.generateOperation(
          method,
          route.path,
          openApiPath
        );
      });
    });

    return this.paths;
  }

  // Convert Express path to OpenAPI format
  expressToOpenAPIPath(expressPath) {
    // Convert :param to {param}
    return expressPath.replace(/:([^/]+)/g, '{$1}');
  }

  // Generate operation object for a route
  generateOperation(method, expressPath, openApiPath) {
    const operation = {
      tags: this.inferTags(expressPath),
      summary: this.generateSummary(method, expressPath),
      description: this.generateDescription(method, expressPath),
      operationId: this.generateOperationId(method, expressPath),
      parameters: this.extractParameters(openApiPath),
      responses: this.generateResponses(method, expressPath)
    };

    // Add request body for POST, PUT, PATCH
    if (['post', 'put', 'patch'].includes(method)) {
      operation.requestBody = this.generateRequestBody(method, expressPath);
    }

    // Add security if applicable
    if (this.requiresAuth(expressPath)) {
      operation.security = [{ bearerAuth: [] }];
    }

    return operation;
  }

  // Infer tags from path
  inferTags(path) {
    const segments = path.split('/').filter(s => s && !s.startsWith(':'));
    if (segments.length > 0) {
      const tag = segments[0].charAt(0).toUpperCase() + segments[0].slice(1);

      // Add tag to tags list if not exists
      if (!this.tags.find(t => t.name === tag)) {
        this.tags.push({
          name: tag,
          description: `Operations related to ${tag.toLowerCase()}`
        });
      }

      return [tag];
    }
    return ['General'];
  }

  // Generate operation summary
  generateSummary(method, path) {
    const resource = path.split('/').filter(s => s && !s.startsWith(':')).pop() || 'resource';
    const summaries = {
      get: path.includes(':') ? `Get ${resource} by ID` : `List all ${resource}`,
      post: `Create a new ${resource}`,
      put: `Update ${resource}`,
      patch: `Partially update ${resource}`,
      delete: `Delete ${resource}`,
      head: `Check ${resource} existence`,
      options: `Get ${resource} options`
    };
    return summaries[method] || `${method.toUpperCase()} ${resource}`;
  }

  // Generate detailed description
  generateDescription(method, path) {
    const resource = path.split('/').filter(s => s && !s.startsWith(':')).pop() || 'resource';
    return `Performs a ${method.toUpperCase()} operation on ${resource}. ` +
           `This endpoint ${this.requiresAuth(path) ? 'requires authentication' : 'is publicly accessible'}.`;
  }

  // Generate operation ID
  generateOperationId(method, path) {
    const segments = path.split('/').filter(s => s && !s.startsWith(':'));
    const resource = segments.join('_');
    return `${method}_${resource}`.replace(/[^a-zA-Z0-9_]/g, '_');
  }

  // Extract parameters from path
  extractParameters(path) {
    const parameters = [];

    // Extract path parameters
    const pathParams = path.match(/{([^}]+)}/g);
    if (pathParams) {
      pathParams.forEach(param => {
        const name = param.slice(1, -1);
        parameters.push({
          name,
          in: 'path',
          required: true,
          description: `ID of the ${name}`,
          schema: {
            type: 'string',
            format: name.toLowerCase().includes('id') ? 'uuid' : undefined
          }
        });
      });
    }

    // Add common query parameters for list operations
    if (!path.includes('{')) {
      parameters.push(
        {
          name: 'page',
          in: 'query',
          description: 'Page number for pagination',
          schema: { type: 'integer', default: 1, minimum: 1 }
        },
        {
          name: 'limit',
          in: 'query',
          description: 'Number of items per page',
          schema: { type: 'integer', default: 20, minimum: 1, maximum: 100 }
        },
        {
          name: 'sort',
          in: 'query',
          description: 'Sort field and direction (e.g., "name:asc")',
          schema: { type: 'string' }
        },
        {
          name: 'filter',
          in: 'query',
          description: 'Filter criteria',
          schema: { type: 'string' }
        }
      );
    }

    return parameters;
  }

  // Generate request body schema
  generateRequestBody(method, path) {
    const resource = path.split('/').filter(s => s && !s.startsWith(':')).pop() || 'resource';
    const schemaName = resource.charAt(0).toUpperCase() + resource.slice(1);

    // Generate or retrieve schema
    if (!this.schemas[schemaName]) {
      this.schemas[schemaName] = this.generateResourceSchema(schemaName);
    }

    return {
      required: method === 'post',
      content: {
        'application/json': {
          schema: {
            $ref: `#/components/schemas/${schemaName}`
          },
          examples: this.generateExamples(schemaName)
        }
      }
    };
  }

  // Generate resource schema
  generateResourceSchema(name) {
    // This would ideally extract from your actual models
    // Here's a generic example
    return {
      type: 'object',
      required: ['name'],
      properties: {
        id: {
          type: 'string',
          format: 'uuid',
          readOnly: true,
          description: 'Unique identifier'
        },
        name: {
          type: 'string',
          minLength: 1,
          maxLength: 255,
          description: `Name of the ${name.toLowerCase()}`
        },
        description: {
          type: 'string',
          maxLength: 1000,
          description: 'Detailed description'
        },
        status: {
          type: 'string',
          enum: ['active', 'inactive', 'pending'],
          default: 'active',
          description: 'Current status'
        },
        metadata: {
          type: 'object',
          additionalProperties: true,
          description: 'Additional metadata'
        },
        createdAt: {
          type: 'string',
          format: 'date-time',
          readOnly: true,
          description: 'Creation timestamp'
        },
        updatedAt: {
          type: 'string',
          format: 'date-time',
          readOnly: true,
          description: 'Last update timestamp'
        }
      }
    };
  }

  // Generate response schemas
  generateResponses(method, path) {
    const responses = {
      '200': {
        description: 'Successful operation',
        content: {
          'application/json': {
            schema: this.generateResponseSchema(method, path)
          }
        }
      },
      '400': {
        description: 'Bad request',
        content: {
          'application/json': {
            schema: { $ref: '#/components/schemas/Error' }
          }
        }
      },
      '401': {
        description: 'Unauthorized',
        content: {
          'application/json': {
            schema: { $ref: '#/components/schemas/Error' }
          }
        }
      },
      '404': {
        description: 'Resource not found',
        content: {
          'application/json': {
            schema: { $ref: '#/components/schemas/Error' }
          }
        }
      },
      '500': {
        description: 'Internal server error',
        content: {
          'application/json': {
            schema: { $ref: '#/components/schemas/Error' }
          }
        }
      }
    };

    // Customize based on method
    if (method === 'post') {
      responses['201'] = responses['200'];
      responses['201'].description = 'Resource created successfully';
      delete responses['200'];
    } else if (method === 'delete') {
      responses['204'] = {
        description: 'Resource deleted successfully'
      };
      delete responses['200'];
    }

    return responses;
  }

  // Generate response schema based on operation
  generateResponseSchema(method, path) {
    const resource = path.split('/').filter(s => s && !s.startsWith(':')).pop() || 'resource';
    const schemaName = resource.charAt(0).toUpperCase() + resource.slice(1);

    if (!path.includes(':') && method === 'get') {
      // List operation
      return {
        type: 'object',
        properties: {
          data: {
            type: 'array',
            items: { $ref: `#/components/schemas/${schemaName}` }
          },
          pagination: { $ref: '#/components/schemas/Pagination' },
          meta: { $ref: '#/components/schemas/Meta' }
        }
      };
    } else {
      // Single resource
      return { $ref: `#/components/schemas/${schemaName}` };
    }
  }

  // Generate examples for schemas
  generateExamples(schemaName) {
    return {
      default: {
        summary: 'Standard example',
        value: {
          name: `Example ${schemaName}`,
          description: 'This is an example description',
          status: 'active',
          metadata: {
            category: 'example',
            priority: 'high'
          }
        }
      },
      minimal: {
        summary: 'Minimal required fields',
        value: {
          name: `Minimal ${schemaName}`
        }
      },
      complete: {
        summary: 'All fields populated',
        value: {
          id: '550e8400-e29b-41d4-a716-446655440000',
          name: `Complete ${schemaName}`,
          description: 'This example includes all possible fields',
          status: 'active',
          metadata: {
            category: 'complete',
            priority: 'high',
            tags: ['example', 'complete'],
            customField: 'custom value'
          },
          createdAt: '2024-01-15T10:30:00Z',
          updatedAt: '2024-01-15T14:45:00Z'
        }
      }
    };
  }

  // Check if path requires authentication
  requiresAuth(path) {
    // Implement your auth logic
    const publicPaths = ['/health', '/docs', '/api-docs', '/login', '/register'];
    return !publicPaths.some(p => path.includes(p));
  }

  // Add common schemas
  addCommonSchemas() {
    this.schemas.Error = {
      type: 'object',
      required: ['code', 'message'],
      properties: {
        code: {
          type: 'string',
          description: 'Error code'
        },
        message: {
          type: 'string',
          description: 'Error message'
        },
        details: {
          type: 'object',
          additionalProperties: true,
          description: 'Additional error details'
        },
        timestamp: {
          type: 'string',
          format: 'date-time',
          description: 'Error timestamp'
        }
      }
    };

    this.schemas.Pagination = {
      type: 'object',
      properties: {
        page: { type: 'integer', minimum: 1 },
        limit: { type: 'integer', minimum: 1, maximum: 100 },
        total: { type: 'integer', minimum: 0 },
        pages: { type: 'integer', minimum: 0 }
      }
    };

    this.schemas.Meta = {
      type: 'object',
      properties: {
        version: { type: 'string' },
        timestamp: { type: 'string', format: 'date-time' },
        requestId: { type: 'string', format: 'uuid' }
      }
    };
  }

  // Generate complete OpenAPI specification
  generateOpenAPISpec() {
    // Analyze routes
    this.analyzeRoutes();

    // Add common schemas
    this.addCommonSchemas();

    const spec = {
      openapi: '3.0.3',
      info: {
        title: this.config.title,
        version: this.config.version,
        description: this.config.description,
        contact: this.config.contact,
        license: this.config.license
      },
      servers: this.config.servers,
      tags: this.tags,
      paths: this.paths,
      components: {
        schemas: this.schemas,
        securitySchemes: {
          bearerAuth: {
            type: 'http',
            scheme: 'bearer',
            bearerFormat: 'JWT'
          },
          apiKey: {
            type: 'apiKey',
            name: 'X-API-Key',
            in: 'header'
          },
          oauth2: {
            type: 'oauth2',
            flows: {
              authorizationCode: {
                authorizationUrl: 'https://auth.example.com/oauth/authorize',
                tokenUrl: 'https://auth.example.com/oauth/token',
                refreshUrl: 'https://auth.example.com/oauth/refresh',
                scopes: {
                  read: 'Read access',
                  write: 'Write access',
                  admin: 'Admin access'
                }
              }
            }
          }
        }
      },
      security: this.config.security
    };

    return spec;
  }

  // Export to various formats
  async exportDocumentation(outputDir = './api-docs') {
    const spec = this.generateOpenAPISpec();

    // Create output directory
    await fs.mkdir(outputDir, { recursive: true });
    await fs.mkdir(path.join(outputDir, 'collections'), { recursive: true });

    // Save OpenAPI spec in YAML and JSON
    await fs.writeFile(
      path.join(outputDir, 'openapi.yaml'),
      yaml.dump(spec, { indent: 2 })
    );

    await fs.writeFile(
      path.join(outputDir, 'openapi.json'),
      JSON.stringify(spec, null, 2)
    );

    // Generate Postman collection
    const postmanCollection = this.convertToPostmanCollection(spec);
    await fs.writeFile(
      path.join(outputDir, 'collections', 'postman_collection.json'),
      JSON.stringify(postmanCollection, null, 2)
    );

    // Generate HTML documentation
    await this.generateHTMLDocs(spec, outputDir);

    // Generate README
    await this.generateREADME(spec, outputDir);

    console.log(`API documentation generated in ${outputDir}`);
    return spec;
  }

  // Convert OpenAPI to Postman Collection
  convertToPostmanCollection(openApiSpec) {
    const collection = {
      info: {
        name: openApiSpec.info.title,
        description: openApiSpec.info.description,
        version: openApiSpec.info.version,
        schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
      },
      item: [],
      auth: {
        type: 'bearer',
        bearer: [{
          key: 'token',
          value: '{{access_token}}',
          type: 'string'
        }]
      },
      variable: [
        {
          key: 'baseUrl',
          value: openApiSpec.servers[0].url,
          type: 'string'
        },
        {
          key: 'access_token',
          value: '',
          type: 'string'
        }
      ]
    };

    // Group by tags
    const folders = {};

    Object.entries(openApiSpec.paths).forEach(([path, methods]) => {
      Object.entries(methods).forEach(([method, operation]) => {
        const tag = operation.tags?.[0] || 'General';

        if (!folders[tag]) {
          folders[tag] = {
            name: tag,
            item: []
          };
        }

        const request = {
          name: operation.summary,
          request: {
            method: method.toUpperCase(),
            header: [],
            url: {
              raw: `{{baseUrl}}${path}`,
              host: ['{{baseUrl}}'],
              path: path.split('/').filter(p => p)
            },
            description: operation.description
          }
        };

        // Add path parameters
        if (operation.parameters) {
          const pathParams = operation.parameters.filter(p => p.in === 'path');
          const queryParams = operation.parameters.filter(p => p.in === 'query');

          if (pathParams.length > 0) {
            request.request.url.variable = pathParams.map(p => ({
              key: p.name,
              value: '',
              description: p.description
            }));
          }

          if (queryParams.length > 0) {
            request.request.url.query = queryParams.map(p => ({
              key: p.name,
              value: '',
              description: p.description,
              disabled: !p.required
            }));
          }
        }

        // Add request body
        if (operation.requestBody) {
          const content = operation.requestBody.content['application/json'];
          if (content) {
            request.request.header.push({
              key: 'Content-Type',
              value: 'application/json'
            });

            request.request.body = {
              mode: 'raw',
              raw: JSON.stringify(
                content.examples?.default?.value || {},
                null,
                2
              )
            };
          }
        }

        // Add auth if required
        if (operation.security) {
          request.request.auth = {
            type: 'bearer',
            bearer: [{
              key: 'token',
              value: '{{access_token}}'
            }]
          };
        }

        folders[tag].item.push(request);
      });
    });

    collection.item = Object.values(folders);
    return collection;
  }

  // Generate HTML documentation
  async generateHTMLDocs(spec, outputDir) {
    // Swagger UI HTML
    const swaggerHtml = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>${spec.info.title} - Swagger UI</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui.css">
    <style>
      body { margin: 0; padding: 0; }
      .swagger-ui .topbar { display: none; }
    </style>
</head>
<body>
    <div id="swagger-ui"></div>
    <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui-bundle.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui-standalone-preset.js"></script>
    <script>
      window.onload = () => {
        window.ui = SwaggerUIBundle({
          url: './openapi.json',
          dom_id: '#swagger-ui',
          presets: [
            SwaggerUIBundle.presets.apis,
            SwaggerUIStandalonePreset
          ],
          layout: "BaseLayout",
          deepLinking: true,
          showExtensions: true,
          showCommonExtensions: true,
          tryItOutEnabled: true
        });
      };
    </script>
</body>
</html>`;

    // Redoc HTML
    const redocHtml = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>${spec.info.title} - ReDoc</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body { margin: 0; padding: 0; }
    </style>
</head>
<body>
    <redoc spec-url='./openapi.json'></redoc>
    <script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0/bundles/redoc.standalone.js"></script>
</body>
</html>`;

    await fs.writeFile(path.join(outputDir, 'index.html'), swaggerHtml);
    await fs.writeFile(path.join(outputDir, 'redoc.html'), redocHtml);
  }

  // Generate README documentation
  async generateREADME(spec, outputDir) {
    const readme = `# ${spec.info.title}

${spec.info.description}

**Version:** ${spec.info.version}

## Base URL

\`\`\`
${spec.servers.map(s => s.url).join('\n')}
\`\`\`

## Authentication

This API uses the following authentication methods:
${spec.components.securitySchemes ? Object.entries(spec.components.securitySchemes).map(([name, scheme]) =>
  `- **${name}**: ${scheme.type} ${scheme.scheme || ''}`
).join('\n') : '- No authentication required'}

## Available Endpoints

${Object.entries(spec.paths).map(([path, methods]) =>
  Object.entries(methods).map(([method, op]) =>
    `- \`${method.toUpperCase()} ${path}\` - ${op.summary}`
  ).join('\n')
).join('\n')}

## Quick Start

### Installation

\`\`\`bash
# Using npm
npm install axios

# Using curl
curl -X GET "${spec.servers[0].url}/endpoint"
\`\`\`

### Example Request

\`\`\`javascript
const axios = require('axios');

const config = {
  method: 'get',
  url: '${spec.servers[0].url}${Object.keys(spec.paths)[0]}',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
  }
};

axios(config)
  .then(response => {
    console.log(JSON.stringify(response.data));
  })
  .catch(error => {
    console.error(error);
  });
\`\`\`

## Documentation

- [Swagger UI](./index.html) - Interactive API documentation
- [ReDoc](./redoc.html) - Alternative documentation format
- [OpenAPI Spec](./openapi.json) - Raw OpenAPI specification
- [Postman Collection](./collections/postman_collection.json) - Import to Postman

## SDKs

Generate client SDKs using:

\`\`\`bash
# JavaScript/TypeScript
npx @openapitools/openapi-generator-cli generate -i openapi.yaml -g typescript-axios -o ./sdk/typescript

# Python
openapi-generator-cli generate -i openapi.yaml -g python -o ./sdk/python

# Go
openapi-generator-cli generate -i openapi.yaml -g go -o ./sdk/go
\`\`\`

## Support

${spec.info.contact?.email ? `For support, contact ${spec.info.contact.email}` : 'For support, please refer to the documentation.'}

## License

${spec.info.license?.name || 'See LICENSE file'}
`;

    await fs.writeFile(path.join(outputDir, 'README.md'), readme);
  }

  // Setup interactive documentation routes
  setupDocumentationRoutes(basePath = '/api-docs') {
    // Serve OpenAPI spec
    this.app.get(`${basePath}/openapi.json`, (req, res) => {
      res.json(this.generateOpenAPISpec());
    });

    // Serve Swagger UI
    this.app.use(
      `${basePath}`,
      swaggerUi.serve,
      swaggerUi.setup(this.generateOpenAPISpec(), {
        customCss: '.swagger-ui .topbar { display: none }',
        customSiteTitle: this.config.title
      })
    );

    console.log(`API documentation available at ${basePath}`);
  }
}

// Usage example
const app = express();

const docGenerator = new APIDocumentationGenerator(app, {
  title: 'E-commerce API',
  version: '2.0.0',
  description: 'Complete e-commerce platform API with user management, products, and orders',
  servers: [
    { url: 'https://api.example.com/v2', description: 'Production' },
    { url: 'https://staging-api.example.com/v2', description: 'Staging' },
    { url: 'http://localhost:3000', description: 'Development' }
  ],
  contact: {
    name: 'API Support',
    email: 'api@example.com',
    url: 'https://support.example.com'
  }
});

// Generate and export documentation
docGenerator.exportDocumentation('./api-documentation');

// Setup interactive docs
docGenerator.setupDocumentationRoutes('/docs');

module.exports = APIDocumentationGenerator;

Example 2: Python FastAPI with Automatic OpenAPI Enhancement

# api_doc_enhancer.py
from fastapi import FastAPI, APIRouter, Depends, HTTPException
from fastapi.openapi.utils import get_openapi
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
import json
import yaml
from pathlib import Path
import httpx
from datetime import datetime

class OpenAPIEnhancer:
    """Enhance FastAPI's auto-generated OpenAPI with additional features"""

    def __init__(self, app: FastAPI):
        self.app = app
        self.examples = {}
        self.additional_schemas = {}
        self.external_docs = {}

    def enhance_openapi(self):
        """Enhance the OpenAPI spec with additional information"""
        if self.app.openapi_schema:
            return self.app.openapi_schema

        openapi_schema = get_openapi(
            title=self.app.title,
            version=self.app.version,
            description=self.app.description,
            routes=self.app.routes,
        )

        # Add custom enhancements
        self._add_api_versioning(openapi_schema)
        self._add_rate_limiting_info(openapi_schema)
        self._add_webhook_definitions(openapi_schema)
        self._add_code_samples(openapi_schema)
        self._add_security_schemes(openapi_schema)
        self._add_server_variables(openapi_schema)
        self._enhance_schemas(openapi_schema)
        self._add_tags_metadata(openapi_schema)

        self.app.openapi_schema = openapi_schema
        return self.app.openapi_schema

    def _add_api_versioning(self, spec: Dict[str, Any]):
        """Add API versioning information"""
        spec["info"]["x-api-versioning"] = {
            "strategy": "uri",
            "current": "v2",
            "supported": ["v1", "v2"],
            "deprecated": ["v0"],
            "sunset": {
                "v0": "2024-01-01",
                "v1": "2025-01-01"
            }
        }

    def _add_rate_limiting_info(self, spec: Dict[str, Any]):
        """Add rate limiting documentation"""
        spec["info"]["x-rate-limiting"] = {
            "default": {
                "requests": 1000,
                "window": "1h"
            },
            "authenticated": {
                "requests": 5000,
                "window": "1h"
            },
            "endpoints": {
                "/api/search": {
                    "requests": 100,
                    "window": "1m"
                }
            },
            "headers": {
                "limit": "X-RateLimit-Limit",
                "remaining": "X-RateLimit-Remaining",
                "reset": "X-RateLimit-Reset"
            }
        }

    def _add_webhook_definitions(self, spec: Dict[str, Any]):
        """Add webhook definitions to OpenAPI spec"""
        spec["webhooks"] = {
            "orderCreated": {
                "post": {
                    "requestBody": {
                        "description": "Order creation notification",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/OrderWebhook"
                                }
                            }
                        }
                    },
                    "responses": {
                        "200": {
                            "description": "Webhook processed successfully"
                        }
                    }
                }
            },
            "paymentProcessed": {
                "post": {
                    "requestBody": {
                        "description": "Payment processing notification",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PaymentWebhook"
                                }
                            }
                        }
                    },
                    "responses": {
                        "200": {
                            "description": "Webhook acknowledged"
                        }
                    }
                }
            }
        }

        # Add webhook schemas
        if "components" not in spec:
            spec["components"] = {}
        if "schemas" not in spec["components"]:
            spec["components"]["schemas"] = {}

        spec["components"]["schemas"]["OrderWebhook"] = {
            "type": "object",
            "required": ["event", "data", "timestamp"],
            "properties": {
                "event": {
                    "type": "string",
                    "enum": ["order.created", "order.updated", "order.cancelled"]
                },
                "data": {
                    "type": "object",
                    "properties": {
                        "orderId": {"type": "string", "format": "uuid"},
                        "customerId": {"type": "string", "format": "uuid"},
                        "amount": {"type": "number", "format": "float"},
                        "status": {"type": "string"}
                    }
                },
                "timestamp": {
                    "type": "string",
                    "format": "date-time"
                }
            }
        }

    def _add_code_samples(self, spec: Dict[str, Any]):
        """Add code samples to each endpoint"""
        for path, methods in spec.get("paths", {}).items():
            for method, operation in methods.items():
                if method in ["get", "post", "put", "delete", "patch"]:
                    operation["x-code-samples"] = self._generate_code_samples(
                        method, path, operation
                    )

    def _generate_code_samples(self, method: str, path: str, operation: Dict) -> List[Dict]:
        """Generate code samples for multiple languages"""
        samples = []

        # cURL example
        curl_sample = f"curl -X {method.upper()} '{self.app.servers[0]['url'] if hasattr(self.app, 'servers') else 'https://api.example.com'}{path}'"
        if method in ["post", "put", "patch"]:
            curl_sample += """ \\
  -H 'Content-Type: application/json' \\
  -H 'Authorization: Bearer YOUR_TOKEN' \\
  -d '{
    "name": "example",
    "value": 123
  }'"""
        else:
            curl_sample += " \\\n  -H 'Authorization: Bearer YOUR_TOKEN'"

        samples.append({
            "lang": "Shell",
            "source": curl_sample,
            "label": "cURL"
        })

        # Python example
        python_sample = f"""import requests

url = "https://api.example.com{path}"
headers = {{
    "Authorization": "Bearer YOUR_TOKEN",
    "Content-Type": "application/json"
}}"""

        if method in ["post", "put", "patch"]:
            python_sample += """
data = {
    "name": "example",
    "value": 123
}
response = requests.""" + method + """(url, json=data, headers=headers)"""
        else:
            python_sample += f"""
response = requests.{method}(url, headers=headers)"""

        python_sample += """
print(response.json())"""

        samples.append({
            "lang": "Python",
            "source": python_sample,
            "label": "Python (requests)"
        })

        # JavaScript example
        js_sample = f"""const axios = require('axios');

const config = {{
  method: '{method}',
  url: 'https://api.example.com{path}',
  headers: {{
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json'
  }}"""

        if method in ["post", "put", "patch"]:
            js_sample += """,
  data: {
    name: 'example',
    value: 123
  }"""

        js_sample += """
};

axios(config)
  .then(response => {
    console.log(JSON.stringify(response.data));
  })
  .catch(error => {
    console.error(error);
  });"""

        samples.append({
            "lang": "JavaScript",
            "source": js_sample,
            "label": "Node.js (axios)"
        })

        # Go example
        go_sample = f"""package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {{
    url := "https://api.example.com{path}"

"""

        if method in ["post", "put", "patch"]:
            go_sample += """    payload := map[string]interface{}{
        "name": "example",
        "value": 123,
    }

    jsonData, _ := json.Marshal(payload)
    req, _ := http.NewRequest(""" + f'"{method.upper()}", url, bytes.NewBuffer(jsonData))'
        else:
            go_sample += f"""    req, _ := http.NewRequest("{method.upper()}", url, nil)"""

        go_sample += """
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}"""

        samples.append({
            "lang": "Go",
            "source": go_sample,
            "label": "Go (net/http)"
        })

        return samples

    def _add_security_schemes(self, spec: Dict[str, Any]):
        """Add comprehensive security schemes"""
        if "components" not in spec:
            spec["components"] = {}

        spec["components"]["securitySchemes"] = {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
                "description": "JWT token authentication"
            },
            "apiKey": {
                "type": "apiKey",
                "name": "X-API-Key",
                "in": "header",
                "description": "API key authentication"
            },
            "oauth2": {
                "type": "oauth2",
                "flows": {
                    "authorizationCode": {
                        "authorizationUrl": "https://auth.example.com/oauth/authorize",
                        "tokenUrl": "https://auth.example.com/oauth/token",
                        "refreshUrl": "https://auth.example.com/oauth/refresh",
                        "scopes": {
                            "read": "Read access to protected resources",
                            "write": "Write access to protected resources",
                            "admin": "Admin access to all resources"
                        }
                    },
                    "clientCredentials": {
                        "tokenUrl": "https://auth.example.com/oauth/token",
                        "scopes": {
                            "api": "API access"
                        }
                    }
                }
            },
            "cookieAuth": {
                "type": "apiKey",
                "in": "cookie",
                "name": "session_id",
                "description": "Session cookie authentication"
            }
        }

    def _add_server_variables(self, spec: Dict[str, Any]):
        """Add server variables for environment switching"""
        spec["servers"] = [
            {
                "url": "https://{environment}.api.example.com/{version}",
                "description": "API Server",
                "variables": {
                    "environment": {
                        "enum": ["production", "staging", "development"],
                        "default": "production",
                        "description": "Server environment"
                    },
                    "version": {
                        "enum": ["v1", "v2", "v3"],
                        "default": "v2",
                        "description": "API version"
                    }
                }
            },
            {
                "url": "http://localhost:{port}",
                "description": "Local development server",
                "variables": {
                    "port": {
                        "enum": ["3000", "8000", "8080"],
                        "default": "8000",
                        "description": "Server port"
                    }
                }
            }
        ]

    def _enhance_schemas(self, spec: Dict[str, Any]):
        """Enhance schemas with examples and additional properties"""
        for schema_name, schema in spec.get("components", {}).get("schemas", {}).items():
            if "properties" in schema:
                # Add examples to properties
                for prop_name, prop in schema["properties"].items():
                    if "example" not in prop:
                        prop["example"] = self._generate_example_for_type(prop)

                # Add schema-level example
                if "example" not in schema:
                    schema["example"] = {
                        prop_name: prop.get("example")
                        for prop_name, prop in schema["properties"].items()
                    }

    def _generate_example_for_type(self, prop: Dict) -> Any:
        """Generate example based on property type"""
        prop_type = prop.get("type", "string")
        prop_format = prop.get("format", "")

        examples = {
            ("string", "email"): "user@example.com",
            ("string", "date"): "2024-01-15",
            ("string", "date-time"): "2024-01-15T10:30:00Z",
            ("string", "uuid"): "550e8400-e29b-41d4-a716-446655440000",
            ("string", "uri"): "https://example.com/resource",
            ("string", "hostname"): "api.example.com",
            ("string", "ipv4"): "192.168.1.1",
            ("string", "ipv6"): "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
            ("integer", ""): 42,
            ("number", ""): 123.45,
            ("boolean", ""): True,
            ("array", ""): ["item1", "item2"],
            ("object", ""): {"key": "value"}
        }

        return examples.get((prop_type, prop_format), "example")

    def _add_tags_metadata(self, spec: Dict[str, Any]):
        """Add detailed tag descriptions"""
        spec["tags"] = [
            {
                "name": "Authentication",
                "description": "Authentication and authorization endpoints",
                "externalDocs": {
                    "description": "Authentication guide",
                    "url": "https://docs.example.com/auth"
                }
            },
            {
                "name": "Users",
                "description": "User management operations",
                "externalDocs": {
                    "description": "User API documentation",
                    "url": "https://docs.example.com/users"
                }
            },
            {
                "name": "Products",
                "description": "Product catalog management",
                "externalDocs": {
                    "description": "Product API documentation",
                    "url": "https://docs.example.com/products"
                }
            },
            {
                "name": "Orders",
                "description": "Order processing and management",
                "externalDocs": {
                    "description": "Order API documentation",
                    "url": "https://docs.example.com/orders"
                }
            }
        ]

    def export_documentation(self, output_dir: str = "./api-docs"):
        """Export documentation in various formats"""
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)

        # Get enhanced OpenAPI spec
        spec = self.enhance_openapi()

        # Export as JSON
        with open(output_path / "openapi.json", "w") as f:
            json.dump(spec, f, indent=2)

        # Export as YAML
        with open(output_path / "openapi.yaml", "w") as f:
            yaml.dump(spec, f, default_flow_style=False, sort_keys=False)

        # Generate AsyncAPI spec if websockets are used
        asyncapi_spec = self._generate_asyncapi_spec()
        if asyncapi_spec:
            with open(output_path / "asyncapi.yaml", "w") as f:
                yaml.dump(asyncapi_spec, f, default_flow_style=False)

        # Generate Postman collection
        postman_collection = self._convert_to_postman(spec)
        with open(output_path / "postman_collection.json", "w") as f:
            json.dump(postman_collection, f, indent=2)

        # Generate README
        self._generate_readme(spec, output_path)

        print(f"Documentation exported to {output_path}")

    def _generate_asyncapi_spec(self) -> Optional[Dict]:
        """Generate AsyncAPI spec for WebSocket/event-driven APIs"""
        # Check if app has WebSocket routes
        has_websocket = any(
            hasattr(route, "endpoint") and "websocket" in str(route.endpoint)
            for route in self.app.routes
        )

        if not has_websocket:
            return None

        return {
            "asyncapi": "2.6.0",
            "info": {
                "title": f"{self.app.title} WebSocket API",
                "version": self.app.version,
                "description": f"WebSocket API for {self.app.title}"
            },
            "servers": {
                "production": {
                    "url": "wss://api.example.com",
                    "protocol": "ws",
                    "description": "Production WebSocket server"
                }
            },
            "channels": {
                "/ws": {
                    "subscribe": {
                        "message": {
                            "$ref": "#/components/messages/notification"
                        }
                    },
                    "publish": {
                        "message": {
                            "$ref": "#/components/messages/command"
                        }
                    }
                }
            },
            "components": {
                "messages": {
                    "notification": {
                        "payload": {
                            "type": "object",
                            "properties": {
                                "type": {"type": "string"},
                                "data": {"type": "object"}
                            }
                        }
                    },
                    "command": {
                        "payload": {
                            "type": "object",
                            "properties": {
                                "action": {"type": "string"},
                                "params": {"type": "object"}
                            }
                        }
                    }
                }
            }
        }

    def _convert_to_postman(self, spec: Dict) -> Dict:
        """Convert OpenAPI to Postman Collection"""
        # Implementation similar to JavaScript version
        # Simplified for brevity
        return {
            "info": {
                "name": spec["info"]["title"],
                "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
            },
            "item": []
        }

    def _generate_readme(self, spec: Dict, output_path: Path):
        """Generate comprehensive README"""
        readme_content = f"""# {spec['info']['title']}

{spec['info'].get('description', '')}

## Version

{spec['info']['version']}

## Documentation

- [OpenAPI Specification](./openapi.json)
- [Postman Collection](./postman_collection.json)
- [AsyncAPI Specification](./asyncapi.yaml) (if applicable)

## Quick Start

```python
import requests

# Example API call
response = requests.get(
    "https://api.example.com/endpoint",
    headers={{"Authorization": "Bearer YOUR_TOKEN"}}
)
print(response.json())

Authentication

See the OpenAPI specification for detailed authentication information.

Support

{spec['info'].get('contact', {}).get('email', 'support@example.com')} """

    with open(output_path / "README.md", "w") as f:
        f.write(readme_content)

FastAPI app example with enhanced documentation

app = FastAPI( title="E-commerce API", version="2.0.0", description="Comprehensive e-commerce platform API" )

enhancer = OpenAPIEnhancer(app)

Override the default OpenAPI endpoint

@app.get("/openapi.json") async def get_open_api_endpoint(): return enhancer.enhance_openapi()

Export documentation

if name == "main": enhancer.export_documentation()


## Error Handling

| Error | Cause | Solution |
|-------|-------|----------|
| "No routes found" | Empty Express/FastAPI app | Ensure routes are defined before generation |
| "Invalid OpenAPI spec" | Malformed specification | Validate with online validators |
| "Schema extraction failed" | TypeScript/Pydantic issues | Check type definitions are correct |
| "Swagger UI not loading" | CORS or path issues | Check CORS settings and base path configuration |
| "Examples not generated" | Missing schema definitions | Ensure all models have proper schemas |

## Configuration Options

**Basic Usage:**
```bash
/generate-api-docs \
  --framework=express \
  --output=./api-docs \
  --format=openapi3 \
  --include-examples

Available Options:

--framework <type> - Web framework to analyze

  • express - Express.js applications
  • fastapi - FastAPI Python applications
  • spring - Spring Boot applications
  • rails - Ruby on Rails APIs
  • django - Django REST framework

--format <spec> - Output specification format

  • openapi3 - OpenAPI 3.0.3 (default)
  • openapi2 - OpenAPI 2.0 (Swagger)
  • asyncapi - AsyncAPI for event-driven
  • graphql - GraphQL schema

--output <path> - Output directory for documentation

  • Default: ./api-docs

--include-examples - Generate request/response examples

  • Analyzes codebase for real examples
  • Creates synthetic examples from schemas

--interactive-ui <type> - Interactive documentation UI

  • swagger - Swagger UI (default)
  • redoc - ReDoc documentation
  • both - Both UIs
  • none - Static docs only

--auth-docs - Include authentication documentation

  • Extracts auth middleware
  • Documents OAuth flows
  • Includes example tokens

--sdk-generation - Generate client SDKs

  • --languages - Comma-separated list (js,python,go,java)
  • --sdk-output - SDK output directory

--postman - Generate Postman collection

  • Includes environment variables
  • Pre-request scripts
  • Test assertions

--validate - Validate generated specification

  • Checks for completeness
  • Validates examples
  • Tests schema consistency

Best Practices

DO:

  • Keep API documentation in sync with code using CI/CD
  • Include realistic examples from actual API usage
  • Document all error responses and status codes
  • Use consistent naming conventions across endpoints
  • Include rate limiting and pagination information
  • Document deprecated endpoints with sunset dates
  • Provide "Try it out" functionality in docs
  • Version your API documentation alongside code

DON'T:

  • Expose sensitive information in examples
  • Use production data in documentation
  • Skip documenting edge cases and errors
  • Forget to document authentication requirements
  • Leave schemas without descriptions
  • Mix API versions in single documentation

Performance Considerations

  • Large APIs (500+ endpoints) may take time to analyze
  • Schema extraction from TypeScript can be memory-intensive
  • Consider splitting documentation by module for very large APIs
  • Cache generated documentation to avoid regeneration
  • Use CDN for serving interactive documentation

Security Considerations

  • Never include real API keys or tokens in examples
  • Sanitize any production data used in documentation
  • Use separate documentation URLs for internal vs external APIs
  • Implement authentication on documentation endpoints if needed
  • Review generated schemas for accidentally exposed fields

Troubleshooting

Issue: Routes not detected

// Ensure routes are registered before doc generation
app.use('/api', apiRouter);
// Then generate docs
const docs = new APIDocumentationGenerator(app);

Issue: TypeScript types not extracted

# Install required packages
npm install --save-dev typescript ts-json-schema-generator
# Ensure tsconfig.json includes all source files

Issue: Swagger UI CORS errors

// Enable CORS for documentation
app.use('/api-docs', cors({
  origin: '*',
  credentials: true
}));
  • /api-contract-generator - Design-first API specification
  • /api-mock-server - Create mock server from OpenAPI spec
  • /api-sdk-generator - Generate client libraries
  • /api-versioning-manager - Manage API versions
  • /api-testing-framework - Generate tests from spec

Version History

  • v1.0.0 (2024-10): Initial implementation with OpenAPI 3.0 support
  • Planned v1.1.0: GraphQL introspection and AsyncAPI support