名称: 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之前,阅读并内化:
- 项目CLAUDE.md — 架构、约定、优先级
docs/specs/中的相关规范文档 — 驱动API设计的需求- 项目根目录的CONSTITUTION.md — 如果存在,约束API模式
- 现有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风格
参考
- rest-api-template.md - REST API规范模板
- graphql-schema-template.md - GraphQL模式模板