opencode-workflow/design-idea/translate/skills/be-api-design/SKILL.md

12 KiB
Raw Blame History

name description
be-api-design Backend Agent 使用此技能設計 API 規格。根據 PRD 產出 OpenAPI 3.0 規格,包含端點、請求/回應結構、錯誤處理。觸發時機PRD 通過後Stage 4

/be-api-design — API 設計

Backend Agent 使用此技能設計 API 規格。

職責

  1. 分析 PRD 中的功能性需求
  2. 使用 design-an-interface 探索多種 API 設計方案
  3. 設計 RESTful API 端點
  4. 定義請求/回應 schema
  5. 設計錯誤處理機制
  6. 產出 OpenAPI 3.0 規格

輸入

  • PRD 文件 (docs/prd/{date}-{feature}.md)
  • 現有 API 風格 (專案中現有的 API 規格)
  • 資料模型上下文

輸出

  • API 規格文件: docs/api/{date}-{feature}.yaml (OpenAPI 3.0)

流程

讀取 PRD
    ↓
識別資源與操作
    ↓
呼叫 design-an-interface 探索 2-3 種設計方案
    ↓
選定最佳方案
    ↓
定義 OpenAPI Schema
    ↓
設計錯誤處理
    ↓
安全性審查
    ↓
產出 OpenAPI 文件

步驟說明

1. 讀取 PRD

從 PRD 中提取所有功能性需求,識別:

  • 資源(名詞):使用者、訂單、商品等
  • 操作(動詞):建立、讀取、更新、刪除等
  • 關係:資源之間的關聯(一對多、多對多)
  • 非功能性需求:分頁、速率限制、認證等

2. 識別資源與操作

將功能性需求映射為 RESTful 資源:

  • 每個名詞 → 潛在資源
  • 每個動詞 → HTTP method
  • 每個關係 → 巢狀資源或獨立端點

3. 呼叫 design-an-interface

使用 design-an-interface 技能,產生 2-3 種截然不同的 API 設計方案:

  • 方案 A最小化方法數每個資源 1-3 個端點)
  • 方案 B最大化彈性支援多種使用情境
  • 方案 C最佳化最常見的操作

比較各方案的優劣,選定最佳設計。

4. 定義 OpenAPI Schema

使用下方模板產出完整的 OpenAPI 3.0 規格。

5. 安全性審查

確認所有端點都有適當的認證機制和權限控制。

6. 產出文件

儲存至 docs/api/{date}-{feature}.yaml

設計原則

RESTful 設計

資源導向:
  - URL 代表資源,而非動作
  - HTTP 方法代表動作

範例:
  GET    /api/v1/users              # 列出使用者
  GET    /api/v1/users/{id}         # 取得特定使用者
  POST   /api/v1/users              # 建立使用者
  PUT    /api/v1/users/{id}         # 完整更新
  PATCH  /api/v1/users/{id}         # 部分更新
  DELETE /api/v1/users/{id}         # 刪除使用者

巢狀資源:
  GET    /api/v1/users/{id}/orders  # 取得使用者的訂單
  POST   /api/v1/users/{id}/orders  # 為使用者建立訂單

HTTP 狀態碼

成功:
  200: OK                          # GET, PUT, PATCH, DELETE 成功
  201: Created                     # POST 成功建立資源
  204: No Content                  # DELETE 成功,無回應內容

客戶端錯誤:
  400: Bad Request                 # 請求格式錯誤
  401: Unauthorized                # 未認證
  403: Forbidden                   # 無權限
  404: Not Found                   # 資源不存在
  409: Conflict                    # 資源衝突 (如重複)
  422: Unprocessable Entity        # 驗證錯誤
  429: Too Many Requests           # 速率限制

伺服器錯誤:
  500: Internal Server Error       # 伺服器內部錯誤
  502: Bad Gateway                 # 上游服務錯誤
  503: Service Unavailable         # 服務暫時不可用

回應格式

成功回應:
  type: object
  properties:
    data:
      type: object
    meta:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer

錯誤回應:
  type: object
  properties:
    error:
      type: object
      properties:
        code:
          type: string
        message:
          type: string
        details:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string

OpenAPI 3.0 模板

openapi: 3.0.3
info:
  title: {API 名稱}
  version: 1.0.0
  description: |
    {描述}

    Related PRD: {PRD 連結}    

servers:
  - url: https://api.example.com/v1
    description: Production
  - url: https://staging-api.example.com/v1
    description: Staging

paths:
  /users:
    get:
      summary: 列出使用者
      tags:
        - Users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: search
          in: query
          schema:
            type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'

    post:
      summary: 建立使用者
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: 建立成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '422':
          $ref: '#/components/responses/ValidationError'

  /users/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string

    get:
      summary: 取得使用者
      tags:
        - Users
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    put:
      summary: 完整更新使用者
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUserRequest'
      responses:
        '200':
          description: 更新成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'

    patch:
      summary: 部分更新使用者
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchUserRequest'
      responses:
        '200':
          description: 更新成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '404':
          $ref: '#/components/responses/NotFound'

    delete:
      summary: 刪除使用者
      tags:
        - Users
      responses:
        '204':
          description: 刪除成功
        '404':
          $ref: '#/components/responses/NotFound'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          example: "usr_123456"
        email:
          type: string
          format: email
          example: "user@example.com"
        name:
          type: string
          example: "John Doe"
        status:
          type: string
          enum: [active, inactive, suspended]
          example: "active"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - email
        - name
        - status
        - created_at
        - updated_at

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

    UpdateUserRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        name:
          type: string
          minLength: 1
          maxLength: 100
        status:
          type: string
          enum: [active, inactive]

    PatchUserRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        status:
          type: string
          enum: [active, inactive]

    UserResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/User'

    UserListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/User'
        meta:
          type: object
          properties:
            page:
              type: integer
            limit:
              type: integer
            total:
              type: integer
            total_pages:
              type: integer

    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  message:
                    type: string

  responses:
    BadRequest:
      description: 請求格式錯誤
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    Unauthorized:
      description: 未認證
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    NotFound:
      description: 資源不存在
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    ValidationError:
      description: 驗證錯誤
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

    InternalError:
      description: 伺服器內部錯誤
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - bearerAuth: []

設計檢查清單

安全性

  • 所有端點都有適當的認證機制
  • 輸入驗證完整
  • 速率限制設計
  • CORS 配置

效能

  • 分頁支援
  • 快取策略
  • 批次操作支援

可靠性

  • 錯誤回應格式統一
  • 重試機制建議
  • 冪等性設計

一致性

  • 命名規範一致 (資源用名詞複數)
  • 回應格式一致 (data/meta/error 結構)
  • 篩選/排序/分頁參數一致

相依技能

  • 前置: write-a-prd (PRD 通過後)
  • 輔助: design-an-interface (探索多種設計方案)
  • 後續: dba-schema (DB Schema 設計)
  • 退回: 可退回 write-a-prd 修改需求

退回機制

Design Review 退回 API 設計
    ↓
修改 OpenAPI 規格
    ↓
重新提交 (或重新呼叫 design-an-interface)

DBA Agent 發現 Schema 衝突
    ↓
協商調整 domain model 或 API 回應格式
    ↓
更新 OpenAPI 規格