openapi-spec-generation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenAPI Spec Generation

OpenAPI 3.1规范生成

Comprehensive patterns for creating, maintaining, and validating OpenAPI 3.1 specifications for RESTful APIs.
为RESTful API创建、维护和验证OpenAPI 3.1规范的综合方案。

When to Use This Skill

何时使用此技能

  • Creating API documentation from scratch
  • Generating OpenAPI specs from existing code
  • Designing API contracts (design-first approach)
  • Validating API implementations against specs
  • Generating client SDKs from specs
  • Setting up API documentation portals
  • 从头开始创建API文档
  • 从现有代码生成OpenAPI规范
  • 设计API合约(设计优先方法)
  • 根据规范验证API实现
  • 从规范生成客户端SDK
  • 搭建API文档门户

Core Concepts

核心概念

1. OpenAPI 3.1 Structure

1. OpenAPI 3.1结构

yaml
openapi: 3.1.0
info:
  title: API Title
  version: 1.0.0
servers:
  - url: https://api.example.com/v1
paths:
  /resources:
    get: ...
components:
  schemas: ...
  securitySchemes: ...
yaml
openapi: 3.1.0
info:
  title: API Title
  version: 1.0.0
servers:
  - url: https://api.example.com/v1
paths:
  /resources:
    get: ...
components:
  schemas: ...
  securitySchemes: ...

2. Design Approaches

2. 设计方法

ApproachDescriptionBest For
Design-FirstWrite spec before codeNew APIs, contracts
Code-FirstGenerate spec from codeExisting APIs
HybridAnnotate code, generate specEvolving APIs
方法描述适用场景
设计优先(Design-First)在编写代码前先撰写规范新API、合约设计
代码优先(Code-First)从现有代码生成规范已有API
混合模式(Hybrid)为代码添加注解后生成规范迭代中的API

Templates

模板

Template 1: Complete API Specification

模板1:完整的API规范

yaml
openapi: 3.1.0
info:
  title: User Management API
  description: |
    API for managing users and their profiles.

    ## Authentication
    All endpoints require Bearer token authentication.

    ## Rate Limiting
    - 1000 requests per minute for standard tier
    - 10000 requests per minute for enterprise tier
  version: 2.0.0
  contact:
    name: API Support
    email: api-support@example.com
    url: https://docs.example.com
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://api.example.com/v2
    description: Production
  - url: https://staging-api.example.com/v2
    description: Staging
  - url: http://localhost:3000/v2
    description: Local development

tags:
  - name: Users
    description: User management operations
  - name: Profiles
    description: User profile operations
  - name: Admin
    description: Administrative operations

paths:
  /users:
    get:
      operationId: listUsers
      summary: List all users
      description: Returns a paginated list of users with optional filtering.
      tags:
        - Users
      parameters:
        - $ref: "#/components/parameters/PageParam"
        - $ref: "#/components/parameters/LimitParam"
        - name: status
          in: query
          description: Filter by user status
          schema:
            $ref: "#/components/schemas/UserStatus"
        - name: search
          in: query
          description: Search by name or email
          schema:
            type: string
            minLength: 2
            maxLength: 100
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserListResponse"
              examples:
                default:
                  $ref: "#/components/examples/UserListExample"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"
      security:
        - bearerAuth: []

    post:
      operationId: createUser
      summary: Create a new user
      description: Creates a new user account and sends welcome email.
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUserRequest"
            examples:
              standard:
                summary: Standard user
                value:
                  email: user@example.com
                  name: John Doe
                  role: user
              admin:
                summary: Admin user
                value:
                  email: admin@example.com
                  name: Admin User
                  role: admin
      responses:
        "201":
          description: User created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
          headers:
            Location:
              description: URL of created user
              schema:
                type: string
                format: uri
        "400":
          $ref: "#/components/responses/BadRequest"
        "409":
          description: Email already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      security:
        - bearerAuth: []

  /users/{userId}:
    parameters:
      - $ref: "#/components/parameters/UserIdParam"

    get:
      operationId: getUser
      summary: Get user by ID
      tags:
        - Users
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "404":
          $ref: "#/components/responses/NotFound"
      security:
        - bearerAuth: []

    patch:
      operationId: updateUser
      summary: Update user
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateUserRequest"
      responses:
        "200":
          description: User updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
      security:
        - bearerAuth: []

    delete:
      operationId: deleteUser
      summary: Delete user
      tags:
        - Users
        - Admin
      responses:
        "204":
          description: User deleted
        "404":
          $ref: "#/components/responses/NotFound"
      security:
        - bearerAuth: []
        - apiKey: []

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - name
        - status
        - createdAt
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: Unique user identifier
        email:
          type: string
          format: email
          description: User email address
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User display name
        status:
          $ref: "#/components/schemas/UserStatus"
        role:
          type: string
          enum: [user, moderator, admin]
          default: user
        avatar:
          type: string
          format: uri
          nullable: true
        metadata:
          type: object
          additionalProperties: true
          description: Custom metadata
        createdAt:
          type: string
          format: date-time
          readOnly: true
        updatedAt:
          type: string
          format: date-time
          readOnly: true

    UserStatus:
      type: string
      enum: [active, inactive, suspended, pending]
      description: User account status

    CreateUserRequest:
      type: object
      required:
        - email
        - name
      properties:
        email:
          type: string
          format: email
        name:
          type: string
          minLength: 1
          maxLength: 100
        role:
          type: string
          enum: [user, moderator, admin]
          default: user
        metadata:
          type: object
          additionalProperties: true

    UpdateUserRequest:
      type: object
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        status:
          $ref: "#/components/schemas/UserStatus"
        role:
          type: string
          enum: [user, moderator, admin]
        metadata:
          type: object
          additionalProperties: true

    UserListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/User"
        pagination:
          $ref: "#/components/schemas/Pagination"

    Pagination:
      type: object
      required:
        - page
        - limit
        - total
        - totalPages
      properties:
        page:
          type: integer
          minimum: 1
        limit:
          type: integer
          minimum: 1
          maximum: 100
        total:
          type: integer
          minimum: 0
        totalPages:
          type: integer
          minimum: 0
        hasNext:
          type: boolean
        hasPrev:
          type: boolean

    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Error code for programmatic handling
        message:
          type: string
          description: Human-readable error message
        details:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
        requestId:
          type: string
          description: Request ID for support

  parameters:
    UserIdParam:
      name: userId
      in: path
      required: true
      description: User ID
      schema:
        type: string
        format: uuid

    PageParam:
      name: page
      in: query
      description: Page number (1-based)
      schema:
        type: integer
        minimum: 1
        default: 1

    LimitParam:
      name: limit
      in: query
      description: Items per page
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: VALIDATION_ERROR
            message: Invalid request parameters
            details:
              - field: email
                message: Must be a valid email address

    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: UNAUTHORIZED
            message: Authentication required

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: NOT_FOUND
            message: User not found

    RateLimited:
      description: Too many requests
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        Retry-After:
          description: Seconds until rate limit resets
          schema:
            type: integer
        X-RateLimit-Limit:
          description: Request limit per window
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: Remaining requests in window
          schema:
            type: integer

  examples:
    UserListExample:
      value:
        data:
          - id: "550e8400-e29b-41d4-a716-446655440000"
            email: "john@example.com"
            name: "John Doe"
            status: "active"
            role: "user"
            createdAt: "2024-01-15T10:30:00Z"
        pagination:
          page: 1
          limit: 20
          total: 1
          totalPages: 1
          hasNext: false
          hasPrev: false

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token from /auth/login

    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for service-to-service calls

security:
  - bearerAuth: []
yaml
openapi: 3.1.0
info:
  title: User Management API
  description: |
    API for managing users and their profiles.

    ## Authentication
    All endpoints require Bearer token authentication.

    ## Rate Limiting
    - 1000 requests per minute for standard tier
    - 10000 requests per minute for enterprise tier
  version: 2.0.0
  contact:
    name: API Support
    email: api-support@example.com
    url: https://docs.example.com
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://api.example.com/v2
    description: Production
  - url: https://staging-api.example.com/v2
    description: Staging
  - url: http://localhost:3000/v2
    description: Local development

tags:
  - name: Users
    description: User management operations
  - name: Profiles
    description: User profile operations
  - name: Admin
    description: Administrative operations

paths:
  /users:
    get:
      operationId: listUsers
      summary: List all users
      description: Returns a paginated list of users with optional filtering.
      tags:
        - Users
      parameters:
        - $ref: "#/components/parameters/PageParam"
        - $ref: "#/components/parameters/LimitParam"
        - name: status
          in: query
          description: Filter by user status
          schema:
            $ref: "#/components/schemas/UserStatus"
        - name: search
          in: query
          description: Search by name or email
          schema:
            type: string
            minLength: 2
            maxLength: 100
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserListResponse"
              examples:
                default:
                  $ref: "#/components/examples/UserListExample"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"
      security:
        - bearerAuth: []

    post:
      operationId: createUser
      summary: Create a new user
      description: Creates a new user account and sends welcome email.
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUserRequest"
            examples:
              standard:
                summary: Standard user
                value:
                  email: user@example.com
                  name: John Doe
                  role: user
              admin:
                summary: Admin user
                value:
                  email: admin@example.com
                  name: Admin User
                  role: admin
      responses:
        "201":
          description: User created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
          headers:
            Location:
              description: URL of created user
              schema:
                type: string
                format: uri
        "400":
          $ref: "#/components/responses/BadRequest"
        "409":
          description: Email already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
      security:
        - bearerAuth: []

  /users/{userId}:
    parameters:
      - $ref: "#/components/parameters/UserIdParam"

    get:
      operationId: getUser
      summary: Get user by ID
      tags:
        - Users
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "404":
          $ref: "#/components/responses/NotFound"
      security:
        - bearerAuth: []

    patch:
      operationId: updateUser
      summary: Update user
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateUserRequest"
      responses:
        "200":
          description: User updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
      security:
        - bearerAuth: []

    delete:
      operationId: deleteUser
      summary: Delete user
      tags:
        - Users
        - Admin
      responses:
        "204":
          description: User deleted
        "404":
          $ref: "#/components/responses/NotFound"
      security:
        - bearerAuth: []
        - apiKey: []

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
        - name
        - status
        - createdAt
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: Unique user identifier
        email:
          type: string
          format: email
          description: User email address
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: User display name
        status:
          $ref: "#/components/schemas/UserStatus"
        role:
          type: string
          enum: [user, moderator, admin]
          default: user
        avatar:
          type: string
          format: uri
          nullable: true
        metadata:
          type: object
          additionalProperties: true
          description: Custom metadata
        createdAt:
          type: string
          format: date-time
          readOnly: true
        updatedAt:
          type: string
          format: date-time
          readOnly: true

    UserStatus:
      type: string
      enum: [active, inactive, suspended, pending]
      description: User account status

    CreateUserRequest:
      type: object
      required:
        - email
        - name
      properties:
        email:
          type: string
          format: email
        name:
          type: string
          minLength: 1
          maxLength: 100
        role:
          type: string
          enum: [user, moderator, admin]
          default: user
        metadata:
          type: object
          additionalProperties: true

    UpdateUserRequest:
      type: object
      minProperties: 1
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        status:
          $ref: "#/components/schemas/UserStatus"
        role:
          type: string
          enum: [user, moderator, admin]
        metadata:
          type: object
          additionalProperties: true

    UserListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/User"
        pagination:
          $ref: "#/components/schemas/Pagination"

    Pagination:
      type: object
      required:
        - page
        - limit
        - total
        - totalPages
      properties:
        page:
          type: integer
          minimum: 1
        limit:
          type: integer
          minimum: 1
          maximum: 100
        total:
          type: integer
          minimum: 0
        totalPages:
          type: integer
          minimum: 0
        hasNext:
          type: boolean
        hasPrev:
          type: boolean

    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Error code for programmatic handling
        message:
          type: string
          description: Human-readable error message
        details:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
        requestId:
          type: string
          description: Request ID for support

  parameters:
    UserIdParam:
      name: userId
      in: path
      required: true
      description: User ID
      schema:
        type: string
        format: uuid

    PageParam:
      name: page
      in: query
      description: Page number (1-based)
      schema:
        type: integer
        minimum: 1
        default: 1

    LimitParam:
      name: limit
      in: query
      description: Items per page
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: VALIDATION_ERROR
            message: Invalid request parameters
            details:
              - field: email
                message: Must be a valid email address

    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: UNAUTHORIZED
            message: Authentication required

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            code: NOT_FOUND
            message: User not found

    RateLimited:
      description: Too many requests
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
      headers:
        Retry-After:
          description: Seconds until rate limit resets
          schema:
            type: integer
        X-RateLimit-Limit:
          description: Request limit per window
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: Remaining requests in window
          schema:
            type: integer

  examples:
    UserListExample:
      value:
        data:
          - id: "550e8400-e29b-41d4-a716-446655440000"
            email: "john@example.com"
            name: "John Doe"
            status: "active"
            role: "user"
            createdAt: "2024-01-15T10:30:00Z"
        pagination:
          page: 1
          limit: 20
          total: 1
          totalPages: 1
          hasNext: false
          hasPrev: false

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token from /auth/login

    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for service-to-service calls

security:
  - bearerAuth: []

Template 2: Code-First Generation (Python/FastAPI)

模板2:代码优先生成(Python/FastAPI)

python
undefined
python
undefined

FastAPI with automatic OpenAPI generation

FastAPI with automatic OpenAPI generation

from fastapi import FastAPI, HTTPException, Query, Path, Depends from pydantic import BaseModel, Field, EmailStr from typing import Optional, List from datetime import datetime from uuid import UUID from enum import Enum
app = FastAPI( title="User Management API", description="API for managing users and profiles", version="2.0.0", openapi_tags=[ {"name": "Users", "description": "User operations"}, {"name": "Profiles", "description": "Profile operations"}, ], servers=[ {"url": "https://api.example.com/v2", "description": "Production"}, {"url": "http://localhost:8000", "description": "Development"}, ], )
from fastapi import FastAPI, HTTPException, Query, Path, Depends from pydantic import BaseModel, Field, EmailStr from typing import Optional, List from datetime import datetime from uuid import UUID from enum import Enum
app = FastAPI( title="User Management API", description="API for managing users and profiles", version="2.0.0", openapi_tags=[ {"name": "Users", "description": "User operations"}, {"name": "Profiles", "description": "Profile operations"}, ], servers=[ {"url": "https://api.example.com/v2", "description": "Production"}, {"url": "http://localhost:8000", "description": "Development"}, ], )

Enums

Enums

class UserStatus(str, Enum): active = "active" inactive = "inactive" suspended = "suspended" pending = "pending"
class UserRole(str, Enum): user = "user" moderator = "moderator" admin = "admin"
class UserStatus(str, Enum): active = "active" inactive = "inactive" suspended = "suspended" pending = "pending"
class UserRole(str, Enum): user = "user" moderator = "moderator" admin = "admin"

Models

Models

class UserBase(BaseModel): email: EmailStr = Field(..., description="User email address") name: str = Field(..., min_length=1, max_length=100, description="Display name")
class UserCreate(UserBase): role: UserRole = Field(default=UserRole.user) metadata: Optional[dict] = Field(default=None, description="Custom metadata")
model_config = {
    "json_schema_extra": {
        "examples": [
            {
                "email": "user@example.com",
                "name": "John Doe",
                "role": "user"
            }
        ]
    }
}
class UserUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=100) status: Optional[UserStatus] = None role: Optional[UserRole] = None metadata: Optional[dict] = None
class User(UserBase): id: UUID = Field(..., description="Unique identifier") status: UserStatus role: UserRole avatar: Optional[str] = Field(None, description="Avatar URL") metadata: Optional[dict] = None created_at: datetime = Field(..., alias="createdAt") updated_at: Optional[datetime] = Field(None, alias="updatedAt")
model_config = {"populate_by_name": True}
class Pagination(BaseModel): page: int = Field(..., ge=1) limit: int = Field(..., ge=1, le=100) total: int = Field(..., ge=0) total_pages: int = Field(..., ge=0, alias="totalPages") has_next: bool = Field(..., alias="hasNext") has_prev: bool = Field(..., alias="hasPrev")
class UserListResponse(BaseModel): data: List[User] pagination: Pagination
class ErrorDetail(BaseModel): field: str message: str
class ErrorResponse(BaseModel): code: str = Field(..., description="Error code") message: str = Field(..., description="Error message") details: Optional[List[ErrorDetail]] = None request_id: Optional[str] = Field(None, alias="requestId")
class UserBase(BaseModel): email: EmailStr = Field(..., description="User email address") name: str = Field(..., min_length=1, max_length=100, description="Display name")
class UserCreate(UserBase): role: UserRole = Field(default=UserRole.user) metadata: Optional[dict] = Field(default=None, description="Custom metadata")
model_config = {
    "json_schema_extra": {
        "examples": [
            {
                "email": "user@example.com",
                "name": "John Doe",
                "role": "user"
            }
        ]
    }
}
class UserUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=100) status: Optional[UserStatus] = None role: Optional[UserRole] = None metadata: Optional[dict] = None
class User(UserBase): id: UUID = Field(..., description="Unique identifier") status: UserStatus role: UserRole avatar: Optional[str] = Field(None, description="Avatar URL") metadata: Optional[dict] = None created_at: datetime = Field(..., alias="createdAt") updated_at: Optional[datetime] = Field(None, alias="updatedAt")
model_config = {"populate_by_name": True}
class Pagination(BaseModel): page: int = Field(..., ge=1) limit: int = Field(..., ge=1, le=100) total: int = Field(..., ge=0) total_pages: int = Field(..., ge=0, alias="totalPages") has_next: bool = Field(..., alias="hasNext") has_prev: bool = Field(..., alias="hasPrev")
class UserListResponse(BaseModel): data: List[User] pagination: Pagination
class ErrorDetail(BaseModel): field: str message: str
class ErrorResponse(BaseModel): code: str = Field(..., description="Error code") message: str = Field(..., description="Error message") details: Optional[List[ErrorDetail]] = None request_id: Optional[str] = Field(None, alias="requestId")

Endpoints

Endpoints

@app.get( "/users", response_model=UserListResponse, tags=["Users"], summary="List all users", description="Returns a paginated list of users with optional filtering.", responses={ 400: {"model": ErrorResponse, "description": "Invalid request"}, 401: {"model": ErrorResponse, "description": "Unauthorized"}, }, ) async def list_users( page: int = Query(1, ge=1, description="Page number"), limit: int = Query(20, ge=1, le=100, description="Items per page"), status: Optional[UserStatus] = Query(None, description="Filter by status"), search: Optional[str] = Query(None, min_length=2, max_length=100), ): """ List users with pagination and filtering.
- **page**: Page number (1-based)
- **limit**: Number of items per page (max 100)
- **status**: Filter by user status
- **search**: Search by name or email
"""
# Implementation
pass
@app.post( "/users", response_model=User, status_code=201, tags=["Users"], summary="Create a new user", responses={ 400: {"model": ErrorResponse}, 409: {"model": ErrorResponse, "description": "Email already exists"}, }, ) async def create_user(user: UserCreate): """Create a new user and send welcome email.""" pass
@app.get( "/users/{user_id}", response_model=User, tags=["Users"], summary="Get user by ID", responses={404: {"model": ErrorResponse}}, ) async def get_user( user_id: UUID = Path(..., description="User ID"), ): """Retrieve a specific user by their ID.""" pass
@app.patch( "/users/{user_id}", response_model=User, tags=["Users"], summary="Update user", responses={ 400: {"model": ErrorResponse}, 404: {"model": ErrorResponse}, }, ) async def update_user( user_id: UUID = Path(..., description="User ID"), user: UserUpdate = ..., ): """Update user attributes.""" pass
@app.delete( "/users/{user_id}", status_code=204, tags=["Users", "Admin"], summary="Delete user", responses={404: {"model": ErrorResponse}}, ) async def delete_user( user_id: UUID = Path(..., description="User ID"), ): """Permanently delete a user.""" pass
@app.get( "/users", response_model=UserListResponse, tags=["Users"], summary="List all users", description="Returns a paginated list of users with optional filtering.", responses={ 400: {"model": ErrorResponse, "description": "Invalid request"}, 401: {"model": ErrorResponse, "description": "Unauthorized"}, }, ) async def list_users( page: int = Query(1, ge=1, description="Page number"), limit: int = Query(20, ge=1, le=100, description="Items per page"), status: Optional[UserStatus] = Query(None, description="Filter by status"), search: Optional[str] = Query(None, min_length=2, max_length=100), ): """ List users with pagination and filtering.
- **page**: Page number (1-based)
- **limit**: Number of items per page (max 100)
- **status**: Filter by user status
- **search**: Search by name or email
"""
# Implementation
pass
@app.post( "/users", response_model=User, status_code=201, tags=["Users"], summary="Create a new user", responses={ 400: {"model": ErrorResponse}, 409: {"model": ErrorResponse, "description": "Email already exists"}, }, ) async def create_user(user: UserCreate): """Create a new user and send welcome email.""" pass
@app.get( "/users/{user_id}", response_model=User, tags=["Users"], summary="Get user by ID", responses={404: {"model": ErrorResponse}}, ) async def get_user( user_id: UUID = Path(..., description="User ID"), ): """Retrieve a specific user by their ID.""" pass
@app.patch( "/users/{user_id}", response_model=User, tags=["Users"], summary="Update user", responses={ 400: {"model": ErrorResponse}, 404: {"model": ErrorResponse}, }, ) async def update_user( user_id: UUID = Path(..., description="User ID"), user: UserUpdate = ..., ): """Update user attributes.""" pass
@app.delete( "/users/{user_id}", status_code=204, tags=["Users", "Admin"], summary="Delete user", responses={404: {"model": ErrorResponse}}, ) async def delete_user( user_id: UUID = Path(..., description="User ID"), ): """Permanently delete a user.""" pass

Export OpenAPI spec

Export OpenAPI spec

if name == "main": import json print(json.dumps(app.openapi(), indent=2))
undefined
if name == "main": import json print(json.dumps(app.openapi(), indent=2))
undefined

Template 3: Code-First (TypeScript/Express with tsoa)

模板3:代码优先生成(TypeScript/Express with tsoa)

typescript
// tsoa generates OpenAPI from TypeScript decorators

import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Route,
  Path,
  Query,
  Body,
  Response,
  SuccessResponse,
  Tags,
  Security,
  Example,
} from "tsoa";

// Models
interface User {
  /** Unique identifier */
  id: string;
  /** User email address */
  email: string;
  /** Display name */
  name: string;
  status: UserStatus;
  role: UserRole;
  /** Avatar URL */
  avatar?: string;
  /** Custom metadata */
  metadata?: Record<string, unknown>;
  createdAt: Date;
  updatedAt?: Date;
}

enum UserStatus {
  Active = "active",
  Inactive = "inactive",
  Suspended = "suspended",
  Pending = "pending",
}

enum UserRole {
  User = "user",
  Moderator = "moderator",
  Admin = "admin",
}

interface CreateUserRequest {
  email: string;
  name: string;
  role?: UserRole;
  metadata?: Record<string, unknown>;
}

interface UpdateUserRequest {
  name?: string;
  status?: UserStatus;
  role?: UserRole;
  metadata?: Record<string, unknown>;
}

interface Pagination {
  page: number;
  limit: number;
  total: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface UserListResponse {
  data: User[];
  pagination: Pagination;
}

interface ErrorResponse {
  code: string;
  message: string;
  details?: { field: string; message: string }[];
  requestId?: string;
}

@Route("users")
@Tags("Users")
export class UsersController extends Controller {
  /**
   * List all users with pagination and filtering
   * @param page Page number (1-based)
   * @param limit Items per page (max 100)
   * @param status Filter by user status
   * @param search Search by name or email
   */
  @Get()
  @Security("bearerAuth")
  @Response<ErrorResponse>(400, "Invalid request")
  @Response<ErrorResponse>(401, "Unauthorized")
  @Example<UserListResponse>({
    data: [
      {
        id: "550e8400-e29b-41d4-a716-446655440000",
        email: "john@example.com",
        name: "John Doe",
        status: UserStatus.Active,
        role: UserRole.User,
        createdAt: new Date("2024-01-15T10:30:00Z"),
      },
    ],
    pagination: {
      page: 1,
      limit: 20,
      total: 1,
      totalPages: 1,
      hasNext: false,
      hasPrev: false,
    },
  })
  public async listUsers(
    @Query() page: number = 1,
    @Query() limit: number = 20,
    @Query() status?: UserStatus,
    @Query() search?: string,
  ): Promise<UserListResponse> {
    // Implementation
    throw new Error("Not implemented");
  }

  /**
   * Create a new user
   */
  @Post()
  @Security("bearerAuth")
  @SuccessResponse(201, "Created")
  @Response<ErrorResponse>(400, "Invalid request")
  @Response<ErrorResponse>(409, "Email already exists")
  public async createUser(@Body() body: CreateUserRequest): Promise<User> {
    this.setStatus(201);
    throw new Error("Not implemented");
  }

  /**
   * Get user by ID
   * @param userId User ID
   */
  @Get("{userId}")
  @Security("bearerAuth")
  @Response<ErrorResponse>(404, "User not found")
  public async getUser(@Path() userId: string): Promise<User> {
    throw new Error("Not implemented");
  }

  /**
   * Update user attributes
   * @param userId User ID
   */
  @Patch("{userId}")
  @Security("bearerAuth")
  @Response<ErrorResponse>(400, "Invalid request")
  @Response<ErrorResponse>(404, "User not found")
  public async updateUser(
    @Path() userId: string,
    @Body() body: UpdateUserRequest,
  ): Promise<User> {
    throw new Error("Not implemented");
  }

  /**
   * Delete user
   * @param userId User ID
   */
  @Delete("{userId}")
  @Tags("Users", "Admin")
  @Security("bearerAuth")
  @SuccessResponse(204, "Deleted")
  @Response<ErrorResponse>(404, "User not found")
  public async deleteUser(@Path() userId: string): Promise<void> {
    this.setStatus(204);
  }
}
typescript
// tsoa generates OpenAPI from TypeScript decorators

import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Route,
  Path,
  Query,
  Body,
  Response,
  SuccessResponse,
  Tags,
  Security,
  Example,
} from "tsoa";

// Models
interface User {
  /** Unique identifier */
  id: string;
  /** User email address */
  email: string;
  /** Display name */
  name: string;
  status: UserStatus;
  role: UserRole;
  /** Avatar URL */
  avatar?: string;
  /** Custom metadata */
  metadata?: Record<string, unknown>;
  createdAt: Date;
  updatedAt?: Date;
}

enum UserStatus {
  Active = "active",
  Inactive = "inactive",
  Suspended = "suspended",
  Pending = "pending",
}

enum UserRole {
  User = "user",
  Moderator = "moderator",
  Admin = "admin",
}

interface CreateUserRequest {
  email: string;
  name: string;
  role?: UserRole;
  metadata?: Record<string, unknown>;
}

interface UpdateUserRequest {
  name?: string;
  status?: UserStatus;
  role?: UserRole;
  metadata?: Record<string, unknown>;
}

interface Pagination {
  page: number;
  limit: number;
  total: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface UserListResponse {
  data: User[];
  pagination: Pagination;
}

interface ErrorResponse {
  code: string;
  message: string;
  details?: { field: string; message: string }[];
  requestId?: string;
}

@Route("users")
@Tags("Users")
export class UsersController extends Controller {
  /**
   * List all users with pagination and filtering
   * @param page Page number (1-based)
   * @param limit Items per page (max 100)
   * @param status Filter by user status
   * @param search Search by name or email
   */
  @Get()
  @Security("bearerAuth")
  @Response<ErrorResponse>(400, "Invalid request")
  @Response<ErrorResponse>(401, "Unauthorized")
  @Example<UserListResponse>({
    data: [
      {
        id: "550e8400-e29b-41d4-a716-446655440000",
        email: "john@example.com",
        name: "John Doe",
        status: UserStatus.Active,
        role: UserRole.User,
        createdAt: new Date("2024-01-15T10:30:00Z"),
      },
    ],
    pagination: {
      page: 1,
      limit: 20,
      total: 1,
      totalPages: 1,
      hasNext: false,
      hasPrev: false,
    },
  })
  public async listUsers(
    @Query() page: number = 1,
    @Query() limit: number = 20,
    @Query() status?: UserStatus,
    @Query() search?: string,
  ): Promise<UserListResponse> {
    // Implementation
    throw new Error("Not implemented");
  }

  /**
   * Create a new user
   */
  @Post()
  @Security("bearerAuth")
  @SuccessResponse(201, "Created")
  @Response<ErrorResponse>(400, "Invalid request")
  @Response<ErrorResponse>(409, "Email already exists")
  public async createUser(@Body() body: CreateUserRequest): Promise<User> {
    this.setStatus(201);
    throw new Error("Not implemented");
  }

  /**
   * Get user by ID
   * @param userId User ID
   */
  @Get("{userId}")
  @Security("bearerAuth")
  @Response<ErrorResponse>(404, "User not found")
  public async getUser(@Path() userId: string): Promise<User> {
    throw new Error("Not implemented");
  }

  /**
   * Update user attributes
   * @param userId User ID
   */
  @Patch("{userId}")
  @Security("bearerAuth")
  @Response<ErrorResponse>(400, "Invalid request")
  @Response<ErrorResponse>(404, "User not found")
  public async updateUser(
    @Path() userId: string,
    @Body() body: UpdateUserRequest,
  ): Promise<User> {
    throw new Error("Not implemented");
  }

  /**
   * Delete user
   * @param userId User ID
   */
  @Delete("{userId}")
  @Tags("Users", "Admin")
  @Security("bearerAuth")
  @SuccessResponse(204, "Deleted")
  @Response<ErrorResponse>(404, "User not found")
  public async deleteUser(@Path() userId: string): Promise<void> {
    this.setStatus(204);
  }
}

Template 4: Validation & Linting

模板4:验证与代码检查

bash
undefined
bash
undefined

Install validation tools

安装验证工具

npm install -g @stoplight/spectral-cli npm install -g @redocly/cli
npm install -g @stoplight/spectral-cli npm install -g @redocly/cli

Spectral ruleset (.spectral.yaml)

Spectral规则集(.spectral.yaml)

cat > .spectral.yaml << 'EOF' extends: ["spectral:oas", "spectral:asyncapi"]
rules:

Enforce operation IDs

operation-operationId: error

Require descriptions

operation-description: warn info-description: error

Naming conventions

operation-operationId-valid-in-url: true

Security

operation-security-defined: error

Response codes

operation-success-response: error

Custom rules

path-params-snake-case: description: Path parameters should be snake_case severity: warn given: "$.paths[].parameters[?(@.in == 'path')].name" then: function: pattern functionOptions: match: "^[a-z][a-z0-9_]$"
schema-properties-camelCase: description: Schema properties should be camelCase severity: warn given: "$.components.schemas[].properties[]~" then: function: casing functionOptions: type: camel EOF
cat > .spectral.yaml << 'EOF' extends: ["spectral:oas", "spectral:asyncapi"]
rules:

强制要求operation ID

operation-operationId: error

要求添加描述

operation-description: warn info-description: error

命名规范

operation-operationId-valid-in-url: true

安全设置

operation-security-defined: error

响应码

operation-success-response: error

自定义规则

path-params-snake-case: description: Path parameters should be snake_case severity: warn given: "$.paths[].parameters[?(@.in == 'path')].name" then: function: pattern functionOptions: match: "^[a-z][a-z0-9_]$"
schema-properties-camelCase: description: Schema properties should be camelCase severity: warn given: "$.components.schemas[].properties[]~" then: function: casing functionOptions: type: camel EOF

Run Spectral

运行Spectral检查

spectral lint openapi.yaml
spectral lint openapi.yaml

Redocly config (redocly.yaml)

Redocly配置(redocly.yaml)

cat > redocly.yaml << 'EOF' extends:
  • recommended
rules: no-invalid-media-type-examples: error no-invalid-schema-examples: error operation-4xx-response: warn request-mime-type: severity: error allowedValues: - application/json response-mime-type: severity: error allowedValues: - application/json - application/problem+json
theme: openapi: generateCodeSamples: languages: - lang: curl - lang: python - lang: javascript EOF
cat > redocly.yaml << 'EOF' extends:
  • recommended
rules: no-invalid-media-type-examples: error no-invalid-schema-examples: error operation-4xx-response: warn request-mime-type: severity: error allowedValues: - application/json response-mime-type: severity: error allowedValues: - application/json - application/problem+json
theme: openapi: generateCodeSamples: languages: - lang: curl - lang: python - lang: javascript EOF

Run Redocly

运行Redocly

redocly lint openapi.yaml redocly bundle openapi.yaml -o bundled.yaml redocly preview-docs openapi.yaml
undefined
redocly lint openapi.yaml redocly bundle openapi.yaml -o bundled.yaml redocly preview-docs openapi.yaml
undefined

SDK Generation

SDK生成

bash
undefined
bash
undefined

OpenAPI Generator

OpenAPI生成器

npm install -g @openapitools/openapi-generator-cli
npm install -g @openapitools/openapi-generator-cli

Generate TypeScript client

生成TypeScript客户端

openapi-generator-cli generate
-i openapi.yaml
-g typescript-fetch
-o ./generated/typescript-client
--additional-properties=supportsES6=true,npmName=@myorg/api-client
openapi-generator-cli generate
-i openapi.yaml
-g typescript-fetch
-o ./generated/typescript-client
--additional-properties=supportsES6=true,npmName=@myorg/api-client

Generate Python client

生成Python客户端

openapi-generator-cli generate
-i openapi.yaml
-g python
-o ./generated/python-client
--additional-properties=packageName=api_client
openapi-generator-cli generate
-i openapi.yaml
-g python
-o ./generated/python-client
--additional-properties=packageName=api_client

Generate Go client

生成Go客户端

openapi-generator-cli generate
-i openapi.yaml
-g go
-o ./generated/go-client
undefined
openapi-generator-cli generate
-i openapi.yaml
-g go
-o ./generated/go-client
undefined

Best Practices

最佳实践

Do's

建议做法

  • Use $ref - Reuse schemas, parameters, responses
  • Add examples - Real-world values help consumers
  • Document errors - All possible error codes
  • Version your API - In URL or header
  • Use semantic versioning - For spec changes
  • 使用$ref - 复用schema、参数、响应定义
  • 添加示例 - 真实场景的值有助于使用者理解
  • 记录错误 - 涵盖所有可能的错误码
  • 为API版本化 - 在URL或请求头中体现版本
  • 使用语义化版本 - 用于规范的版本更新

Don'ts

不建议做法

  • Don't use generic descriptions - Be specific
  • Don't skip security - Define all schemes
  • Don't forget nullable - Be explicit about null
  • Don't mix styles - Consistent naming throughout
  • Don't hardcode URLs - Use server variables
  • 不要使用通用描述 - 描述要具体明确
  • 不要忽略安全设置 - 定义所有安全方案
  • 不要忘记nullable字段 - 明确标记可为空的字段
  • 不要混合命名风格 - 全程保持一致的命名规范
  • 不要硬编码URL - 使用服务器变量

Resources

参考资源