api-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAPI Design
API设计
Core Principles
核心原则
- Contract-First — Define API spec before implementation
- OpenAPI 3.2 — Use OpenAPI for REST API documentation
- URL Versioning — Version in path , with Sunset headers
/v1/ - Idempotency — PUT/DELETE must be idempotent, POST uses Idempotency-Key
- Cursor Pagination — Avoid offset-based pagination
- RFC 7807 Errors — Standard Problem Details format
- No backwards compatibility — Delete, don't deprecate
- 契约优先 — 在实现前定义API规范
- OpenAPI 3.2 — 使用OpenAPI进行REST API文档编写
- URL版本控制 — 在路径中加入版本号,搭配Sunset响应头
/v1/ - 幂等性 — PUT/DELETE请求必须具备幂等性,POST请求使用Idempotency-Key
- 游标分页 — 避免基于偏移量的分页
- RFC 7807错误格式 — 使用标准的问题详情格式
- 不保留向后兼容性 — 直接删除,而非标记为废弃
Quick Reference
快速参考
When to Use What
选型指南
| Scenario | Choice | Reason |
|---|---|---|
| Public API / MVP | REST | Simple, universal, easy debugging |
| Frontend-driven / Mobile | GraphQL | Fetch exactly what you need |
| Microservices internal | gRPC | High performance, strong typing |
| Real-time data | gRPC / GraphQL Subscriptions | Bidirectional streaming |
| 场景 | 选择 | 原因 |
|---|---|---|
| 公开API / 最小可行产品(MVP) | REST | 简单通用、易于调试 |
| 前端驱动 / 移动端 | GraphQL | 按需获取所需数据 |
| 微服务内部通信 | gRPC | 高性能、强类型约束 |
| 实时数据传输 | gRPC / GraphQL Subscriptions | 双向流式传输 |
REST API Design
REST API设计
Resource Naming
资源命名
undefinedundefinedGood
推荐写法
GET /users # List users
GET /users/123 # Get user
POST /users # Create user
PUT /users/123 # Replace user
PATCH /users/123 # Update user
DELETE /users/123 # Delete user
GET /users # 列出用户
GET /users/123 # 获取单个用户
POST /users # 创建用户
PUT /users/123 # 替换用户资源
PATCH /users/123 # 更新用户资源
DELETE /users/123 # 删除用户
Nested resources
嵌套资源
GET /users/123/orders # User's orders
GET /users/123/orders # 获取用户的订单
Actions (when CRUD doesn't fit)
自定义操作(当CRUD不适用时)
POST /users/123/activate # Action on resource
POST /users/123/activate # 对资源执行激活操作
Query parameters for filtering
查询参数用于过滤
GET /users?status=active&role=admin&limit=20
undefinedGET /users?status=active&role=admin&limit=20
undefinedHTTP Methods
HTTP方法
| Method | Purpose | Idempotent | Safe |
|---|---|---|---|
| GET | Read | Yes | Yes |
| POST | Create | No | No |
| PUT | Replace | Yes | No |
| PATCH | Update | No | No |
| DELETE | Remove | Yes | No |
| 方法 | 用途 | 幂等性 | 安全性 |
|---|---|---|---|
| GET | 读取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 替换资源 | 是 | 否 |
| PATCH | 更新资源 | 否 | 否 |
| DELETE | 删除资源 | 是 | 否 |
Status Codes
状态码
undefinedundefinedSuccess
成功响应
200 OK - Successful GET/PUT/PATCH
201 Created - Successful POST (include Location header)
204 No Content - Successful DELETE
200 OK - 成功的GET/PUT/PATCH请求
201 Created - 成功的POST请求(需包含Location响应头)
204 No Content - 成功的DELETE请求
Client Errors
客户端错误
400 Bad Request - Malformed request syntax
401 Unauthorized - Missing/invalid authentication
403 Forbidden - Authenticated but not authorized
404 Not Found - Resource doesn't exist
409 Conflict - Duplicate/conflict (e.g., unique constraint)
422 Unprocessable - Validation failed
429 Too Many - Rate limited
400 Bad Request - 请求格式错误
401 Unauthorized - 缺少或无效的身份验证信息
403 Forbidden - 已通过身份验证,但无权限访问
404 Not Found - 资源不存在
409 Conflict - 资源冲突(如违反唯一约束)
422 Unprocessable - 请求参数验证失败
429 Too Many - 请求频率超限
Server Errors
服务端错误
500 Internal Error - Unexpected server error
503 Unavailable - Service temporarily down
undefined500 Internal Error - 意外的服务端错误
503 Unavailable - 服务暂时不可用
undefinedError Response (RFC 7807)
错误响应(RFC 7807)
json
{
"type": "https://api.example.com/errors/validation",
"title": "Validation Error",
"status": 422,
"detail": "The request contains invalid parameters",
"instance": "/users/123",
"errors": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "age", "message": "Must be positive integer" }
]
}json
{
"type": "https://api.example.com/errors/validation",
"title": "Validation Error",
"status": 422,
"detail": "The request contains invalid parameters",
"instance": "/users/123",
"errors": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "age", "message": "Must be positive integer" }
]
}Pagination (Cursor-Based)
游标分页
json
// Request
GET /users?limit=20&cursor=eyJpZCI6MTAwfQ
// Response
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"prev_cursor": "eyJpZCI6ODB9",
"has_next": true,
"has_prev": true,
"limit": 20
}
}json
// 请求示例
GET /users?limit=20&cursor=eyJpZCI6MTAwfQ
// 响应示例
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"prev_cursor": "eyJpZCI6ODB9",
"has_next": true,
"has_prev": true,
"limit": 20
}
}Versioning
版本管理
undefinedundefinedURL versioning (recommended)
URL版本控制(推荐方案)
GET /v1/users
GET /v2/users
GET /v1/users
GET /v2/users
Deprecation headers
废弃通知头
Sunset: Sat, 31 Dec 2025 23:59:59 GMT
Deprecation: true
Link: </v2/users>; rel="successor-version"
undefinedSunset: Sat, 31 Dec 2025 23:59:59 GMT
Deprecation: true
Link: </v2/users>; rel="successor-version"
undefinedIdempotency
幂等性实现
undefinedundefinedFor non-idempotent operations (POST)
针对非幂等操作(如POST)
POST /orders
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
POST /orders
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Server stores result and returns same response for duplicate key
服务端存储请求结果,针对相同的Key返回一致的响应
---
---GraphQL Design
GraphQL设计
Schema Principles
架构原则
- Domain-driven — Schema reflects business domain, not database
- Descriptive names — Clear field/type names for monitoring
- Limit nesting — Deep nesting hurts performance
- Use @key — Mark entity identifiers for Federation
- 领域驱动 — 架构反映业务领域,而非数据库结构
- 命名清晰 — 字段/类型名称具有描述性,便于监控
- 限制嵌套层级 — 过深的嵌套会影响性能
- 使用@key — 标记实体标识符以支持Federation
Type Definitions
类型定义
graphql
type Query {
user(id: ID!): User
users(first: Int, after: String, filter: UserFilter): UserConnection!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
}
type User @key(fields: "id") {
id: ID!
email: String!
name: String!
orders(first: Int, after: String): OrderConnection!
createdAt: DateTime!
}graphql
type Query {
user(id: ID!): User
users(first: Int, after: String, filter: UserFilter): UserConnection!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
}
type User @key(fields: "id") {
id: ID!
email: String!
name: String!
orders(first: Int, after: String): OrderConnection!
createdAt: DateTime!
}Relay-style pagination
Relay风格分页
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
undefinedtype UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
undefinedError Handling
错误处理
graphql
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
}graphql
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
}Union for typed errors
使用Union类型处理强类型错误
union CreateUserPayload = User | ValidationError | ConflictError
type ValidationError {
message: String!
field: String
code: String!
}
type ConflictError {
message: String!
existingId: ID!
}
undefinedunion CreateUserPayload = User | ValidationError | ConflictError
type ValidationError {
message: String!
field: String
code: String!
}
type ConflictError {
message: String!
existingId: ID!
}
undefinedN+1 Prevention
N+1问题解决
typescript
// Use DataLoader for batching
const userLoader = new DataLoader(async (ids: string[]) => {
const users = await db.user.findMany({
where: { id: { in: ids } }
});
return ids.map(id => users.find(u => u.id === id));
});
// Resolver
const resolvers = {
Order: {
user: (order) => userLoader.load(order.userId),
},
};typescript
// 使用DataLoader进行批量查询
const userLoader = new DataLoader(async (ids: string[]) => {
const users = await db.user.findMany({
where: { id: { in: ids } }
});
return ids.map(id => users.find(u => u.id === id));
});
// 解析器
const resolvers = {
Order: {
user: (order) => userLoader.load(order.userId),
},
};gRPC Design
gRPC设计
Proto Definition
Proto定义
protobuf
syntax = "proto3";
package api.v1;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service UserService {
// Unary
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
// Server streaming
rpc ListUsers(ListUsersRequest) returns (stream User);
// Client streaming
rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateResponse);
// Bidirectional streaming
rpc SyncUsers(stream UserUpdate) returns (stream UserUpdate);
}
message User {
string id = 1;
string email = 2;
string name = 3;
google.protobuf.Timestamp created_at = 4;
}
message GetUserRequest {
string id = 1;
}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
UserFilter filter = 3;
}
message UserFilter {
optional string status = 1;
optional string role = 2;
}protobuf
syntax = "proto3";
package api.v1;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service UserService {
// 一元调用
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
// 服务端流式调用
rpc ListUsers(ListUsersRequest) returns (stream User);
// 客户端流式调用
rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateResponse);
// 双向流式调用
rpc SyncUsers(stream UserUpdate) returns (stream UserUpdate);
}
message User {
string id = 1;
string email = 2;
string name = 3;
google.protobuf.Timestamp created_at = 4;
}
message GetUserRequest {
string id = 1;
}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
UserFilter filter = 3;
}
message UserFilter {
optional string status = 1;
optional string role = 2;
}Error Handling
错误处理
protobuf
// Use Google's richer error model
import "google/rpc/status.proto";
import "google/rpc/error_details.proto";
// For streaming: embed errors in response
message StreamResponse {
oneof result {
User user = 1;
StreamError error = 2;
}
}
message StreamError {
string code = 1;
string message = 2;
map<string, string> details = 3;
}protobuf
// 使用Google的增强错误模型
import "google/rpc/status.proto";
import "google/rpc/error_details.proto";
// 流式调用:在响应中嵌入错误信息
message StreamResponse {
oneof result {
User user = 1;
StreamError error = 2;
}
}
message StreamError {
string code = 1;
string message = 2;
map<string, string> details = 3;
}Deadlines & Retries
超时与重试
typescript
// Always set deadlines
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 5);
const user = await client.getUser(
{ id: '123' },
{ deadline }
);
// Configure retry policy
const retryPolicy = {
maxAttempts: 3,
initialBackoff: '0.1s',
maxBackoff: '1s',
backoffMultiplier: 2,
retryableStatusCodes: ['UNAVAILABLE', 'DEADLINE_EXCEEDED'],
};typescript
// 始终设置超时时间
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 5);
const user = await client.getUser(
{ id: '123' },
{ deadline }
);
// 配置重试策略
const retryPolicy = {
maxAttempts: 3,
initialBackoff: '0.1s',
maxBackoff: '1s',
backoffMultiplier: 2,
retryableStatusCodes: ['UNAVAILABLE', 'DEADLINE_EXCEEDED'],
};Rate Limiting
请求频率限制
Headers
响应头
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
Retry-After: 60X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
Retry-After: 60Response (429)
429响应示例
json
{
"type": "https://api.example.com/errors/rate-limited",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "You have exceeded the rate limit of 100 requests per minute",
"retryAfter": 60
}json
{
"type": "https://api.example.com/errors/rate-limited",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "You have exceeded the rate limit of 100 requests per minute",
"retryAfter": 60
}Checklist
检查清单
markdown
undefinedmarkdown
undefinedDesign
设计阶段
- API spec defined before implementation
- Resources use plural nouns
- Correct HTTP methods/status codes
- RFC 7807 error format
- 先定义API规范再进行实现
- 资源使用复数名词
- 使用正确的HTTP方法和状态码
- 采用RFC 7807错误格式
Features
功能特性
- Cursor-based pagination
- Rate limiting with headers
- Idempotency keys for POST
- API versioning strategy
- 实现游标分页
- 搭配响应头的请求频率限制
- 为POST请求添加幂等性Key
- 制定API版本管理策略
Documentation
文档
- OpenAPI/GraphQL schema published
- Examples for all endpoints
- Error codes documented
- 发布OpenAPI/GraphQL架构文档
- 为所有端点提供示例
- 记录所有错误码
Operations
运维
- Request/response logging
- Latency and error rate metrics
- Deprecation notices for old versions
---- 记录请求/响应日志
- 监控延迟和错误率指标
- 为旧版本添加废弃通知
---See Also
扩展参考
- reference/rest.md — REST deep dive
- reference/graphql.md — GraphQL patterns
- reference/grpc.md — gRPC patterns
- reference/comparison.md — Selection guide
- reference/rest.md — REST深度解析
- reference/graphql.md — GraphQL设计模式
- reference/grpc.md — gRPC设计模式
- reference/comparison.md — API选型指南