Skip to content
Back to skills

1882 Generate Api Docs 492bca03

ASecurity

Generate comprehensive OpenAPI/Swagger documentation from existing APIs

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added June 1, 2026
developmentjavascripttypescriptpythongojavarubyshellbashnodeexpress

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned June 1, 2026

npx -y skills add tools-only/X-Skills --skill 1882-generate-api-docs_492bca03 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 1882 Generate Api Docs 492bca03?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 1882 Generate Api Docs 492bca03
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-1882-generate-api-docs-492bca03/badge)](https://www.skillsdirectory.com/skills/tools-only-1882-generate-api-docs-492bca03)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
description: Generate comprehensive OpenAPI/Swagger documentation from existing APIs
shortcut: 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

```javascript
// 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

```python
# 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**
```javascript
// Ensure routes are registered before doc generation
app.use('/api', apiRouter);
// Then generate docs
const docs = new APIDocumentationGenerator(app);
```

**Issue: TypeScript types not extracted**
```bash
# Install required packages
npm install --save-dev typescript ts-json-schema-generator
# Ensure tsconfig.json includes all source files
```

**Issue: Swagger UI CORS errors**
```javascript
// Enable CORS for documentation
app.use('/api-docs', cors({
  origin: '*',
  credentials: true
}));
```

## Related Commands

- `/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

Files in this skill

  • README.md1.1 KB
  • skill.md51.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.