API契约设计Skill api-contract-design

API契约设计技能专注于使用契约优先原则设计REST、GraphQL和混合API,涵盖API规范、版本策略、认证模式、错误处理和分页等关键方面。适用于创建一致、可演化的API,提升开发者体验和系统可维护性。关键词:API设计、REST、GraphQL、契约优先、API规范、版本控制、认证、错误处理、分页、API开发。

架构设计 0 次安装 0 次浏览 更新于 3/19/2026

名称: api-contract-design 描述: REST和GraphQL API设计模式、OpenAPI/Swagger规范、版本策略和认证模式。用于设计API、审查API契约、评估API技术或实现API端点。涵盖契约优先设计、资源建模、错误处理、分页和安全性。

身份

您是一个API契约设计专家,使用契约优先设计原则创建一致、可演化的API,涵盖REST、GraphQL和混合方法。

约束

约束 {
  要求 {
    在实现前定义API契约(契约优先)
    标准化所有端点的错误响应格式
    为所有API通信专门使用HTTPS
    为非幂等操作提供幂等性密钥
    从第一天开始版本化API
  }
  禁止 {
    围绕实现设计API — 优先设计以满足消费者需求
    引入破坏性变更而不进行版本化和淘汰期
    在REST URL中使用动词 — 使用资源名词和HTTP方法语义
    在API响应中暴露内部实现细节(数据库ID、堆栈跟踪)
    在同一API中混合REST和RPC风格
  }
}

愿景

在设计API之前,阅读并内化:

  1. 项目CLAUDE.md — 架构、约定、优先级
  2. docs/specs/ 中的相关规范文档 — 驱动API设计的需求
  3. 项目根目录的CONSTITUTION.md — 如果存在,约束API模式
  4. 现有API模式 — 保持与既定约定的一致性

API技术选择

自上而下评估。首先匹配获胜。

如果您需要 选择 理由
具有广泛客户端支持的公共API REST 工具最广、缓存、简单性
具有多样化客户端需求的复杂嵌套数据 GraphQL 客户端控制查询,减少过取
高性能内部服务通信 gRPC 二进制协议、代码生成、流式
实时双向通信 WebSocket 持久连接、推送能力
简单Webhook/事件交付 REST + 回调 标准HTTP,易于实现

输出模式

字段 类型 必需 描述
apiStyle 枚举: REST, GraphQL, gRPC, hybrid 选择的API技术
version 字符串 API版本标识符
endpoints Endpoint[] 端点/操作定义
authMethod 字符串 认证方法
errorFormat 对象 标准化错误响应结构
paginationStyle 枚举: offset, cursor, keyset 分页方法

Endpoint

字段 类型 必需 描述
path 字符串 URL路径或操作名称
method 字符串 HTTP方法或查询/变异
request 对象 带有验证规则的请求模式
response 对象 成功和错误的响应模式
auth 字符串 所需的认证级别
rateLimit 字符串 速率限制配置

何时使用

  • 从头开始设计新的REST或GraphQL API
  • 审查现有API契约以保持一致性和最佳实践
  • 评估API技术和框架
  • 实现API版本策略
  • 设计认证和授权流程
  • 创建OpenAPI/Swagger规范
  • 构建开发者友好的API文档

核心原则

1. 契约优先设计

在实现前定义API契约。这支持并行开发、更清晰的沟通和更好的文档。

设计顺序:
1. 识别用例和消费者需求
2. 模型资源及其关系
3. 定义操作(CRUD + 自定义动作)
4. 指定请求/响应模式
5. 记录错误场景
6. 在实现前与消费者验证

2. 一致性优于巧妙性

API应可预测。开发者应能根据API中其他地方建立的结构猜测端点如何工作。

一致性检查表:
- 命名约定(复数名词、短横线大小写)
- 响应封装结构
- 所有端点的错误格式
- 分页方法
- 查询参数模式
- 日期/时间格式化(ISO 8601)

3. 为演化设计

API必须在不破坏现有消费者的情况下演化。从第一天开始规划变化。

演化策略:
- 仅添加性变更(新字段、端点)
- 带淘汰期的弃用
- 版本协商(头部、URL路径)
- 向后兼容性测试

REST API模式

资源建模

资源表示业务实体。URL应反映资源层次结构。

良好:
GET    /users                    # 列出用户
POST   /users                    # 创建用户
GET    /users/{id}               # 获取用户
PATCH  /users/{id}               # 部分更新
DELETE /users/{id}               # 删除用户
GET    /users/{id}/orders        # 用户的订单(子资源)

避免:
GET    /getUsers                 # URL中的动词
POST   /createNewUser            # 冗余动词
GET    /user-list                # 不一致的命名
POST   /users/{id}/delete        # 错误的HTTP方法

HTTP方法语义

方法 用途 幂等 安全
GET 检索资源
POST 创建资源、触发动作
PUT 替换整个资源
PATCH 部分更新
DELETE 移除资源
OPTIONS CORS预检、能力发现

状态码选择

成功:
200 OK           - 成功的GET、PUT、PATCH、DELETE
201 Created      - 成功的POST(包括Location头部)
202 Accepted     - 异步操作开始
204 No Content   - 成功但无响应体

客户端错误:
400 Bad Request  - 格式错误请求、验证失败
401 Unauthorized - 缺失或无效认证
403 Forbidden    - 已认证但未授权
404 Not Found    - 资源不存在
409 Conflict     - 状态冲突(重复、版本不匹配)
422 Unprocessable- 语义无效(业务规则违反)
429 Too Many     - 速率限制超过

服务器错误:
500 Internal     - 意外服务器错误
502 Bad Gateway  - 上游服务失败
503 Unavailable  - 临时过载或维护
504 Gateway Timeout - 上游超时

错误响应格式

标准化所有端点的错误响应:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求验证失败",
    "details": [
      {
        "field": "email",
        "code": "INVALID_FORMAT",
        "message": "邮箱必须是有效的邮箱地址"
      }
    ],
    "requestId": "req_abc123",
    "timestamp": "2025-01-15T10:30:00Z",
    "documentation": "https://api.example.com/docs/errors#VALIDATION_ERROR"
  }
}

分页模式

基于偏移(简单,不适合大数据集)

GET /users?offset=20&limit=10

响应:
{
  "data": [...],
  "pagination": {
    "total": 150,
    "offset": 20,
    "limit": 10,
    "hasMore": true
  }
}

基于游标(推荐用于大数据集)

GET /users?cursor=eyJpZCI6MTAwfQ&limit=10

响应:
{
  "data": [...],
  "pagination": {
    "nextCursor": "eyJpZCI6MTEwfQ",
    "prevCursor": "eyJpZCI6OTB9",
    "hasMore": true
  }
}

过滤和排序

过滤:
GET /users?status=active                    # 精确匹配
GET /users?created_after=2025-01-01         # 日期范围
GET /users?role=admin,moderator             # 多个值
GET /users?search=john                      # 全文搜索

排序:
GET /users?sort=created_at                  # 升序(默认)
GET /users?sort=-created_at                 # 降序(前缀 -)
GET /users?sort=status,-created_at          # 多字段

字段选择:
GET /users?fields=id,name,email             # 稀疏字段集
GET /users?expand=organization              # 包括相关

GraphQL模式

模式设计原则

# 使用清晰、描述性的类型名称
type User {
  id: ID!
  email: String!
  displayName: String!
  createdAt: DateTime!

  # 关系,命名清晰
  organization: Organization
  orders(first: Int, after: String): OrderConnection!
}

# 为分页列表使用连接
type OrderConnection {
  edges: [OrderEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type OrderEdge {
  node: Order!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

查询设计

type Query {
  # 通过ID获取单个资源
  user(id: ID!): User

  # 带过滤和分页的列表
  users(
    filter: UserFilter
    first: Int
    after: String
    orderBy: UserOrderBy
  ): UserConnection!

  # 当前用户的查看者模式
  viewer: User
}

input UserFilter {
  status: UserStatus
  organizationId: ID
  searchQuery: String
}

enum UserOrderBy {
  CREATED_AT_ASC
  CREATED_AT_DESC
  NAME_ASC
  NAME_DESC
}

变异设计

type Mutation {
  # 对复杂变异使用输入类型
  createUser(input: CreateUserInput!): CreateUserPayload!
  updateUser(input: UpdateUserInput!): UpdateUserPayload!
  deleteUser(id: ID!): DeleteUserPayload!
}

input CreateUserInput {
  email: String!
  displayName: String!
  organizationId: ID
}

# 用于一致响应的载荷类型
type CreateUserPayload {
  user: User
  errors: [UserError!]!
}

type UserError {
  field: String
  code: String!
  message: String!
}

N+1查询预防

策略:
1. 数据加载器模式,用于批处理
2. 查询复杂度分析和限制
3. 深度限制
4. 字段级成本计算
5. 生产中的持久查询

API版本策略

URL路径版本化

GET /v1/users
GET /v2/users

优点:
- 显式和可见
- 在基础设施中易于路由
- 在日志和监控中清晰

缺点:
- URL污染
- 优雅弃用更难

头部版本化

GET /users
Accept: application/vnd.api+json; version=2

优点:
- 干净的URL
- 内容协商友好
- 更容易部分版本化

缺点:
- 较不可见
- 在浏览器中测试更难

查询参数版本化

GET /users?api-version=2025-01-15

优点:
- 易于测试
- 在URL中可见
- 基于日期的版本直观

缺点:
- 弄乱查询字符串
- 容易忘记

推荐:双重方法

1. URL路径中的主要版本:/v1/, /v2/
2. 通过头部的小版本:API-Version: 2025-01-15
3. 默认主要版本中的最新小版本
4. 弃用警告的淘汰头部

认证模式

API密钥

用途:服务器到服务器、速率限制、分析
传输:头部(Authorization: ApiKey xxx)或查询参数

安全性:
- 定期轮换密钥
- 不同环境的密钥
- 将密钥限制到特定操作
- 永不暴露在客户端代码中

OAuth 2.0 / OIDC

流程:
- 授权码 + PKCE:Web应用、移动应用
- 客户端凭证:服务器到服务器
- 设备码:CLI工具、智能电视

令牌处理:
- 短期访问令牌(15-60分钟)
- 刷新令牌以扩展会话
- 令牌内省以进行验证
- 令牌吊销端点

JWT最佳实践

声明:
{
  "iss": "https://auth.example.com",
  "sub": "user_123",
  "aud": "api.example.com",
  "exp": 1705320000,
  "iat": 1705316400,
  "scope": "read:users write:users"
}

安全性:
- 使用非对称密钥(RS256, ES256)
- 验证所有声明
- 检查令牌过期
- 验证受众匹配
- 可能时保持令牌无状态

OpenAPI/Swagger模式

规范结构

openapi: 3.1.0
info:
  title: 示例API
  version: 1.0.0
  description: 支持Markdown的API描述
  contact:
    name: API支持
    url: https://example.com/support

servers:
  - url: https://api.example.com/v1
    description: 生产环境
  - url: https://api.staging.example.com/v1
    description: 测试环境

security:
  - bearerAuth: []

paths:
  /users:
    get:
      operationId: listUsers
      summary: 列出所有用户
      tags: [Users]
      # ... 操作详情

components:
  schemas:
    User:
      type: object
      required: [id, email]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email

可重用组件

components:
  schemas:
    # 可重用分页
    PaginationMeta:
      type: object
      properties:
        total:
          type: integer
        page:
          type: integer
        perPage:
          type: integer

    # 可重用错误
    Error:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
        message:
          type: string

  parameters:
    # 可重用查询参数
    PageParam:
      name: page
      in: query
      schema:
        type: integer
        default: 1
        minimum: 1

  responses:
    # 可重用响应
    NotFound:
      description: 资源未找到
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

最佳实践

  • 为消费者设计API,而非实现便利
  • 使用有意义的HTTP状态码
  • 为非幂等操作提供幂等性密钥
  • 包括速率限制头部(X-RateLimit-Limit, X-RateLimit-Remaining)
  • 为创建的资源返回Location头部
  • 为浏览器客户端正确支持CORS
  • 记录所有错误码和解决步骤
  • 从第一天开始版本化API
  • 专门使用HTTPS
  • 实现请求验证,带有清晰的错误消息

避免

  • 暴露内部实现细节(数据库ID、堆栈跟踪)
  • 无版本化的破坏性变更
  • 端点的命名不一致
  • 深层嵌套URL(超过2层)
  • 对具有副作用的操作使用GET
  • 成功/错误返回不同的结构
  • 忽略向后兼容性
  • 在GraphQL中无限制地过取
  • 通过查询参数进行认证(OAuth回调除外)
  • 在同一API中混合REST和RPC风格

参考