名称: api-design-fundamentals 描述: 用于设计API、在REST/GraphQL/gRPC之间选择,或理解API设计最佳实践时使用。涵盖协议选择、资源建模和API模式。 允许工具: Read, Glob, Grep
API设计基础
设计有效API的指南,包括协议选择、资源建模和最佳实践。
何时使用此技能
- 在REST、GraphQL和gRPC之间选择
- 设计资源模型和端点
- 理解API设计最佳实践
- 创建一致的API约定
- 为开发者体验设计
协议比较
REST(表述性状态转移)
最适合: CRUD操作、公共API、广泛客户端兼容性
特点:
- 资源导向(名词,非动词)
- HTTP方法映射到操作(GET、POST、PUT、DELETE)
- 无状态
- 可缓存的响应
- 自描述消息
示例:
GET /users - 列出用户
GET /users/{id} - 获取用户
POST /users - 创建用户
PUT /users/{id} - 更新用户
DELETE /users/{id} - 删除用户
优点:
- 简单,广泛理解
- 优秀的缓存支持
- 与任何HTTP客户端兼容
- 适合公共API
缺点:
- 过度获取(获取超出需要的数据)
- 不足获取(需要多个请求)
- 无内置模式/类型
GraphQL
最适合: 复杂数据需求、移动应用、聚合多个服务
特点:
- 单一端点
- 客户端指定所需数据
- 强类型模式
- 内省支持
- 通过订阅支持实时
示例:
查询 {
用户(id: "123") {
名称
邮箱
帖子(限制: 5) {
标题
评论 { 数量 }
}
}
}
优点:
- 无过度/不足获取
- 强类型和模式
- 优秀的开发者工具
- 无版本演化
缺点:
- 缓存复杂性
- N+1查询问题
- 学习曲线
- 不适合简单API
gRPC
最适合: 内部微服务、高性能、多语言系统
特点:
- 协议缓冲区(二进制格式)
- HTTP/2传输
- 双向流
- 代码生成
- 强类型
示例(proto):
服务 用户服务 {
rpc 获取用户(获取用户请求) 返回 (用户);
rpc 列出用户(列出用户请求) 返回 (流 用户);
rpc 创建用户(创建用户请求) 返回 (用户);
}
优点:
- 高性能(二进制、HTTP/2)
- 强合约(protobuf)
- 双向流
- 适合微服务
缺点:
- 浏览器支持有限(需grpc-web)
- 非人类可读
- 学习曲线较陡
- 调试更复杂
协议选择指南
决策树:
这是面向外部开发者的公共API吗?
├── 是 → REST(最广泛兼容性)
└── 否
└── 客户端需要灵活查询吗?
├── 是 → GraphQL
└── 否
└── 性能关键吗?
├── 是 → gRPC
└── 否 → REST或GraphQL
| 因素 | REST | GraphQL | gRPC |
|---|---|---|---|
| 公共API | ✅ 最佳 | ⚠️ 可能 | ❌ 差 |
| 移动应用 | ⚠️ 可 | ✅ 最佳 | ⚠️ 有限 |
| 微服务 | ⚠️ 可 | ⚠️ 可 | ✅ 最佳 |
| 实时 | ⚠️ WebSocket | ✅ 订阅 | ✅ 流 |
| 浏览器支持 | ✅ 原生 | ✅ 原生 | ⚠️ grpc-web |
| 缓存 | ✅ 易 | ⚠️ 复杂 | ❌ 手动 |
| 学习曲线 | ✅ 低 | ⚠️ 中 | ⚠️ 中 |
REST API设计最佳实践
资源命名
做:
- 使用名词,非动词:/users, /orders, /products
- 使用复数形式:/users(非/user)
- 使用连字符:/user-profiles(非/userProfiles)
- 嵌套关系:/users/{id}/orders
不做:
- /getUsers, /createOrder(动词)
- /user(单数)
- /user_profiles(URL中的蛇形命名)
HTTP方法
| 方法 | 目的 | 幂等 | 安全 |
|---|---|---|---|
| GET | 读取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 替换资源 | 是 | 否 |
| PATCH | 部分更新 | 否* | 否 |
| DELETE | 删除资源 | 是 | 否 |
*PATCH根据实现可能幂等
状态码
| 代码 | 含义 | 何时使用 |
|---|---|---|
| 200 | OK | 成功GET、PUT、PATCH |
| 201 | 已创建 | 成功POST |
| 204 | 无内容 | 成功DELETE |
| 400 | 错误请求 | 无效请求体 |
| 401 | 未授权 | 缺失/无效认证 |
| 403 | 禁止 | 权限不足 |
| 404 | 未找到 | 资源不存在 |
| 409 | 冲突 | 资源冲突 |
| 422 | 不可处理 | 验证失败 |
| 429 | 过多请求 | 速率限制 |
| 500 | 服务器错误 | 意外错误 |
分页
基于偏移(简单,但在大规模有问题):
GET /users?offset=20&limit=10
基于游标(推荐用于大型数据集):
GET /users?cursor=eyJpZCI6MTAwfQ&limit=10
响应:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTEwfQ",
"has_more": true
}
}
过滤和排序
过滤:
GET /products?category=electronics&price_min=100&price_max=500
排序:
GET /products?sort=price:asc,name:desc
字段选择(部分响应):
GET /users?fields=id,name,email
错误响应
{
"error": {
"code": "VALIDATION_ERROR",
"message": "无效请求参数",
"details": [
{
"field": "email",
"message": "无效邮箱格式"
}
],
"request_id": "req_abc123"
}
}
GraphQL最佳实践
模式设计
# 使用清晰、描述性类型
类型 用户 {
id: ID!
email: String!
profile: 用户档案
posts(first: Int, after: String): 帖子连接!
}
# 使用连接进行分页
类型 帖子连接 {
edges: [帖子边!]!
pageInfo: 页信息!
}
# 使用输入类型进行突变
输入 创建用户输入 {
email: String!
name: String!
}
查询复杂度限制
防止昂贵查询:
- 深度限制(最大嵌套级别)
- 复杂度评分(为字段分配成本)
- 查询超时
- 按客户端速率限制
N+1预防
使用DataLoader模式:
- 批量同类型请求
- 单个请求内缓存
- 防止N+1数据库查询
gRPC最佳实践
服务设计
// 保持消息专注
消息 用户 {
string id = 1;
string email = 2;
string name = 3;
}
// 使用请求/响应包装器
消息 获取用户请求 {
string id = 1;
}
消息 获取用户响应 {
用户 user = 1;
}
// 支持流处理大型数据集
服务 用户服务 {
rpc 获取用户(获取用户请求) 返回 (获取用户响应);
rpc 列出用户(列出用户请求) 返回 (流 用户);
}
错误处理
使用标准gRPC状态码:
- OK (0): 成功
- INVALID_ARGUMENT (3): 错误请求
- NOT_FOUND (5): 资源缺失
- PERMISSION_DENIED (7): 禁止
- INTERNAL (13): 服务器错误
- UNAVAILABLE (14): 服务不可用
API进化
向后兼容规则
安全更改(向后兼容):
- 添加新端点
- 添加可选字段
- 添加新枚举值(在末尾)
- 放宽验证规则
破坏性更改(避免):
- 移除端点
- 移除字段
- 更改字段类型
- 重命名字段
- 添加必填字段
弃用策略
1. 标记为弃用(添加头部/注释)
2. 文档迁移路径
3. 设置日落日期
4. 监控使用
5. 日落后移除
相关技能
rate-limiting-patterns- API保护idempotency-patterns- 可靠APIapi-versioning- API进化