DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Auto-Generate OpenAPI Docs with Claude Code: Code-First API Design

API documentation that drifts from the actual code is worse than no docs. Claude Code can generate and maintain OpenAPI specs from your TypeScript types and Zod schemas — keeping docs always in sync.


CLAUDE.md for API Documentation

## API Documentation Rules

### OpenAPI Standard
- Version: OpenAPI 3.1
- Output: docs/openapi.yaml
- Code-first only: auto-generate from code (no hand-written YAML)

### Code Annotations
- TypeScript types become schemas
- JSDoc comments provide descriptions
- @example tags provide examples

### Endpoint Requirements
- All endpoints need summary and description
- All responses typed (200/400/401/403/500)
- Pagination format: { data, total, page, limit }

### Tools
- zod-to-openapi (Zod schemas → OpenAPI)
- swagger-ui-express (Swagger UI for dev)
Enter fullscreen mode Exit fullscreen mode

Generating the zod-to-openapi Setup

Set up zod-to-openapi to auto-generate OpenAPI 3.1 spec from Zod schemas.

Existing schemas:
- CreateUserSchema: { email: string, name: string, role: 'admin' | 'user' }
- UserSchema: { id: string, email: string, name: string, role: string, createdAt: string }

Requirements:
- OpenAPI 3.1
- description and example on all fields
- JWT Bearer authentication
- Standard error response: { error: string, details?: object }

Generate: src/docs/schemas.ts → docs/openapi.yaml
Enter fullscreen mode Exit fullscreen mode

Generated Schema with Annotations

// src/docs/schemas.ts
import { extendZodWithOpenApi } from 'zod-to-openapi';
import { z } from 'zod';

extendZodWithOpenApi(z);

export const CreateUserSchema = z
  .object({
    email: z
      .string()
      .email()
      .openapi({ description: "User's email address", example: 'user@example.com' }),
    name: z
      .string()
      .min(1)
      .max(100)
      .openapi({ description: 'Display name', example: 'John Doe' }),
    role: z
      .enum(['admin', 'user'])
      .openapi({ description: 'User role', example: 'user' }),
  })
  .openapi('CreateUserRequest');

export const UserSchema = z
  .object({
    id: z.string().uuid().openapi({ description: 'User ID' }),
    email: z.string().email(),
    name: z.string(),
    role: z.enum(['admin', 'user']),
    createdAt: z.string().datetime(),
  })
  .openapi('User');
Enter fullscreen mode Exit fullscreen mode

Generating Endpoint Annotations

Add OpenAPI annotations to these Express endpoints:

- POST /users (create user)
- GET /users (paginated list)
- GET /users/:id (get user)
- PATCH /users/:id (partial update)
- DELETE /users/:id (soft delete)

For each endpoint add:
- summary, description, tags
- requestBody (referencing Zod schemas)
- responses (200/201/400/401/403/404/500 — ALL of them)
- security (for JWT-protected endpoints)
Enter fullscreen mode Exit fullscreen mode

Integrating Swagger UI

Add Swagger UI to the Express app.

Requirements:
- Path: /api/docs
- Disabled in production (NODE_ENV=production)
- Dark theme
- Persist authorization (so API key stays set between page refreshes)

Packages: swagger-ui-express, js-yaml
Enter fullscreen mode Exit fullscreen mode
import swaggerUi from 'swagger-ui-express';
import YAML from 'js-yaml';
import fs from 'fs';

if (process.env.NODE_ENV !== 'production') {
  const openapiSpec = YAML.load(
    fs.readFileSync('./docs/openapi.yaml', 'utf8')
  ) as object;

  app.use(
    '/api/docs',
    swaggerUi.serve,
    swaggerUi.setup(openapiSpec, {
      customCss: '.swagger-ui .topbar { display: none }',
      swaggerOptions: { persistAuthorization: true },
    })
  );
}
Enter fullscreen mode Exit fullscreen mode

Enforcing Sync in CI

Add a CI step that fails if the OpenAPI spec is out of date.

Requirements:
- Regenerate OpenAPI during build
- Compare with committed docs/openapi.yaml
- Fail if diff detected
- Error message: "Run `npm run generate:openapi` and commit the changes"
Enter fullscreen mode Exit fullscreen mode

This makes it impossible to merge code without updating the docs.


Summary

Keep OpenAPI docs always in sync with Claude Code:

  1. CLAUDE.md — Define code-first standard, zod-to-openapi as tool
  2. Annotated Zod schemas — descriptions, examples, deprecation flags
  3. Full response coverage — 400/401/403/404/500, not just happy path
  4. Swagger UI — Dev-only, with auth persistence
  5. CI enforcement — Docs drift = failed build

Code Review Pack (¥980) includes /code-review for API documentation review — missing responses, unannotated schemas, inconsistent formats.

👉 prompt-works.jp

Myouga (@myougatheaxo) — Claude Code engineer focused on production API design.

Top comments (0)