api-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAPI Design
API设计
RESTful Resource Naming
RESTful资源命名
Conventions
约定规则
| Rule | Good | Bad |
|---|---|---|
| Use plural nouns | | |
| Use nouns, not verbs | | |
| Nest for relationships | | |
| Use kebab-case | | |
| Keep URLs shallow (max 3) | | |
| Use query params for filters | | |
| Collection + resource IDs | | |
| 规则 | 示例(规范) | 示例(不规范) |
|---|---|---|
| 使用复数名词 | | |
| 使用名词而非动词 | | |
| 嵌套表示资源关系 | | |
| 使用短横线分隔命名(kebab-case) | | |
| 保持URL层级简洁(最多3层) | | |
| 使用查询参数实现过滤 | | |
| 集合+资源ID格式 | | |
URL Structure
URL结构示例
https://api.example.com/v1/users # Collection
https://api.example.com/v1/users/123 # Single resource
https://api.example.com/v1/users/123/orders # Nested collection
https://api.example.com/v1/users/123/orders/456 # Nested resource
https://api.example.com/v1/orders?status=pending # Filtered collectionhttps://api.example.com/v1/users # 资源集合
https://api.example.com/v1/users/123 # 单个资源
https://api.example.com/v1/users/123/orders # 嵌套资源集合
https://api.example.com/v1/users/123/orders/456 # 嵌套单个资源
https://api.example.com/v1/orders?status=pending # 过滤后的资源集合Actions That Do Not Map to CRUD
非CRUD操作的处理方式
For operations that are not simple CRUD, use a sub-resource or action noun:
POST /users/123/activate # State transition
POST /orders/456/refund # Domain action
POST /reports/export # Process trigger对于不对应简单CRUD的操作,使用子资源或动作名词:
POST /users/123/activate # 状态转换
POST /orders/456/refund # 领域动作
POST /reports/export # 流程触发HTTP Method Semantics
HTTP方法语义
| Method | Purpose | Idempotent | Safe | Request Body | Success Code |
|---|---|---|---|---|---|
| Retrieve resource(s) | Yes | Yes | No | 200 |
| Create a resource | No | No | Yes | 201 |
| Full replacement | Yes | No | Yes | 200 |
| Partial update | No* | No | Yes | 200 |
| Remove a resource | Yes | No | No | 204 |
* can be made idempotent with proper implementation but is not guaranteed by the spec.
PATCH| 方法 | 用途 | 幂等性 | 安全性 | 请求体 | 成功状态码 |
|---|---|---|---|---|---|
| 获取资源(单个或多个) | 是 | 是 | 无 | 200 |
| 创建新资源 | 否 | 否 | 有 | 201 |
| 完整替换资源 | 是 | 否 | 有 | 200 |
| 部分更新资源 | 否* | 否 | 有 | 200 |
| 删除资源 | 是 | 否 | 无 | 204 |
*通过合理实现可让具备幂等性,但规范本身不做保证。
PATCHMethod Usage Rules
方法使用规则
- must never modify server state
GET - is the only method for creating new resources
POST - sends the complete resource; omitted fields are set to defaults or null
PUT - sends only the fields to change; omitted fields remain unchanged
PATCH - returns 204 on success, and is a no-op if the resource already does not exist
DELETE
- 绝对不能修改服务器状态
GET - 是创建新资源的唯一方法
POST - 需发送完整资源对象;未传递的字段会被设为默认值或null
PUT - 仅发送需要修改的字段;未传递的字段保持不变
PATCH - 成功时返回204;若资源已不存在,执行无操作即可
DELETE
Examples
示例
http
GET /api/v1/users/123
Accept: application/json
---
POST /api/v1/users
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@example.com"
}
---
PUT /api/v1/users/123
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@newdomain.com",
"role": "admin"
}
---
PATCH /api/v1/users/123
Content-Type: application/json
{
"role": "admin"
}
---
DELETE /api/v1/users/123http
GET /api/v1/users/123
Accept: application/json
---
POST /api/v1/users
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@example.com"
}
---
PUT /api/v1/users/123
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@newdomain.com",
"role": "admin"
}
---
PATCH /api/v1/users/123
Content-Type: application/json
{
"role": "admin"
}
---
DELETE /api/v1/users/123Status Code Guide
状态码指南
Success Codes
成功状态码
| Code | Name | When to Use |
|---|---|---|
| OK | Successful GET, PUT, PATCH, or DELETE with body |
| Created | Successful POST that created a resource |
| No Content | Successful DELETE or PUT with no response body |
| 状态码 | 名称 | 使用场景 |
|---|---|---|
| OK | GET、PUT、PATCH或DELETE请求成功且返回响应体 |
| Created | POST请求成功且创建了新资源 |
| No Content | DELETE或PUT请求成功且无响应体返回 |
Client Error Codes
客户端错误状态码
| Code | Name | When to Use |
|---|---|---|
| Bad Request | Malformed JSON, invalid syntax |
| Unauthorized | Missing or invalid authentication credentials |
| Forbidden | Authenticated but not authorized for this action |
| Not Found | Resource does not exist at this URL |
| Conflict | Resource state conflict (duplicate, version mismatch) |
| Unprocessable Entity | Valid JSON but fails business validation |
| Too Many Requests | Rate limit exceeded |
| 状态码 | 名称 | 使用场景 |
|---|---|---|
| Bad Request | JSON格式错误、语法无效 |
| Unauthorized | 缺少或无效的认证凭证 |
| Forbidden | 已认证但无此操作权限 |
| Not Found | 请求的资源在该URL不存在 |
| Conflict | 资源状态冲突(重复、版本不匹配) |
| Unprocessable Entity | JSON格式合法但不符合业务规则 |
| Too Many Requests | 超出请求频率限制 |
Server Error Codes
服务器错误状态码
| Code | Name | When to Use |
|---|---|---|
| Internal Server Error | Unexpected server failure |
| Service Unavailable | Server is down for maintenance or overloaded |
| 状态码 | 名称 | 使用场景 |
|---|---|---|
| Internal Server Error | 服务器意外故障 |
| Service Unavailable | 服务器维护中或过载 |
Decision Tree
决策树
Is the request well-formed?
No --> 400 Bad Request
Yes --> Is the client authenticated?
No --> 401 Unauthorized
Yes --> Is the client authorized?
No --> 403 Forbidden
Yes --> Does the resource exist?
No --> 404 Not Found
Yes --> Does the request pass validation?
No --> 422 Unprocessable Entity
Yes --> Is there a conflict?
No --> 2xx Success
Yes --> 409 Conflict请求格式是否正确?
否 --> 400 Bad Request
是 --> 客户端是否已认证?
否 --> 401 Unauthorized
是 --> 客户端是否有权限?
否 --> 403 Forbidden
是 --> 资源是否存在?
否 --> 404 Not Found
是 --> 请求是否通过验证?
否 --> 422 Unprocessable Entity
是 --> 是否存在状态冲突?
否 --> 2xx 成功
是 --> 409 ConflictStructured Error Response Format
结构化错误响应格式
All error responses must follow this format:
json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The request could not be processed due to validation errors.",
"details": [
{
"field": "email",
"message": "Must be a valid email address.",
"code": "INVALID_FORMAT"
},
{
"field": "age",
"message": "Must be at least 18.",
"code": "OUT_OF_RANGE"
}
],
"request_id": "req_abc123def456"
}
}所有错误响应必须遵循以下格式:
json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The request could not be processed due to validation errors.",
"details": [
{
"field": "email",
"message": "Must be a valid email address.",
"code": "INVALID_FORMAT"
},
{
"field": "age",
"message": "Must be at least 18.",
"code": "OUT_OF_RANGE"
}
],
"request_id": "req_abc123def456"
}
}Error Response Rules
错误响应规则
- Always include a machine-readable (uppercase snake_case)
code - Always include a human-readable
message - Include array for field-level validation errors
details - Include for traceability
request_id - Never expose stack traces, internal paths, or database details
- Use consistent error codes across the entire API
- 始终包含机器可读的(大写蛇形命名)
code - 始终包含人类可读的
message - 字段级验证错误需包含数组
details - 包含用于问题追踪
request_id - 绝对不能暴露堆栈跟踪、内部路径或数据库细节
- 整个API使用统一的错误码
Standard Error Codes
标准错误码
| Error Code | HTTP Status | Description |
|---|---|---|
| 422 | One or more fields are invalid |
| 404 | Requested resource does not exist |
| 401 | No valid credentials provided |
| 403 | Insufficient permissions |
| 409 | Resource state conflict |
| 429 | Too many requests |
| 500 | Unexpected server error |
| 503 | Dependency or server is down |
| 错误码 | HTTP状态码 | 描述 |
|---|---|---|
| 422 | 一个或多个字段无效 |
| 404 | 请求的资源不存在 |
| 401 | 未提供有效凭证 |
| 403 | 权限不足 |
| 409 | 资源状态冲突 |
| 429 | 请求频率超限 |
| 500 | 服务器意外错误 |
| 503 | 依赖服务或服务器不可用 |
Pagination
分页
Cursor-Based Pagination (Preferred)
基于游标的分页(推荐)
Best for real-time data, large datasets, and consistent results.
Request:
http
GET /api/v1/orders?limit=20&cursor=eyJpZCI6MTAwfQResponse:
json
{
"data": [ ... ],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"has_more": true
}
}Implementation notes:
- Encode cursor as base64 of the last item's sort key
- Cursor is opaque to the client; never expose internal IDs directly
- Always include boolean
has_more
适用于实时数据、大型数据集和结果一致性要求高的场景。
请求示例:
http
GET /api/v1/orders?limit=20&cursor=eyJpZCI6MTAwfQ响应示例:
json
{
"data": [ ... ],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"has_more": true
}
}实现注意事项:
- 游标编码为最后一项排序键的base64值
- 游标对客户端是透明的;绝对不能直接暴露内部ID
- 始终包含布尔值
has_more
Offset-Based Pagination
基于偏移量的分页
Simpler but has consistency issues on changing data.
Request:
http
GET /api/v1/products?page=3&per_page=25Response:
json
{
"data": [ ... ],
"pagination": {
"page": 3,
"per_page": 25,
"total_count": 342,
"total_pages": 14
}
}实现简单,但数据变化时结果一致性存在问题。
请求示例:
http
GET /api/v1/products?page=3&per_page=25响应示例:
json
{
"data": [ ... ],
"pagination": {
"page": 3,
"per_page": 25,
"total_count": 342,
"total_pages": 14
}
}When to Use Each
场景选择
| Approach | Use When |
|---|---|
| Cursor-based | Real-time data, infinite scroll, large datasets |
| Offset-based | Admin panels, reports where total count is needed |
| 分页方式 | 适用场景 |
|---|---|
| 基于游标 | 实时数据、无限滚动、大型数据集 |
| 基于偏移量 | 管理面板、需要总计数的报表场景 |
Pagination Rules
分页规则
- Default or
limitto a sensible value (e.g., 20)per_page - Enforce a maximum limit (e.g., 100) to prevent abuse
- Always include pagination metadata in the response
- Return an empty array (not null) when no results match
data
- 为或
limit设置合理默认值(如20)per_page - 强制设置最大限制(如100)防止滥用
- 响应中始终包含分页元数据
- 无匹配结果时返回空数组(而非null)
data
Versioning Strategies
版本控制策略
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URL path | | Explicit, easy to route | URL changes on version bump |
| Header | | Clean URLs | Hidden, harder to test |
| Query param | | Easy to add | Not standard, clutters query |
| 策略 | 示例 | 优点 | 缺点 |
|---|---|---|---|
| URL路径版本 | | 明确、易于路由 | 版本升级时URL会变更 |
| 请求头版本 | | URL简洁 | 不直观、测试难度高 |
| 查询参数版本 | | 易于添加 | 非标准、污染查询参数 |
Recommended: URL Path Versioning
推荐方案:URL路径版本控制
/api/v1/users # Version 1
/api/v2/users # Version 2 (breaking changes)/api/v1/users # 版本1
/api/v2/users # 版本2(包含破坏性变更)Versioning Rules
版本控制规则
- Only increment the major version for breaking changes
- Support at least N-1 version in production
- Document the deprecation timeline (minimum 6 months notice)
- Use header to communicate deprecation date
Sunset - Non-breaking changes (new fields, new endpoints) do not require a version bump
- 仅在有破坏性变更时升级主版本号
- 生产环境至少支持前一个版本(N-1)
- 明确文档化弃用时间线(至少提前6个月通知)
- 使用响应头告知弃用日期
Sunset - 非破坏性变更(新增字段、新增接口)无需升级版本
Rate Limiting
请求限流
Headers
响应头
Include these headers in every response:
| Header | Value |
|---|---|
| Maximum requests per window |
| Requests remaining in current window |
| Unix timestamp when the window resets |
| Seconds to wait (on 429 responses only) |
所有响应需包含以下头信息:
| 响应头 | 值 |
|---|---|
| 时间窗口内的最大请求数 |
| 当前时间窗口剩余请求数 |
| 时间窗口重置的Unix时间戳 |
| 需等待的秒数(仅在429响应中返回) |
Example 429 Response
429响应示例
http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000000
Retry-After: 30
Content-Type: application/json
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the rate limit. Please retry after 30 seconds.",
"request_id": "req_xyz789"
}
}http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000000
Retry-After: 30
Content-Type: application/json
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the rate limit. Please retry after 30 seconds.",
"request_id": "req_xyz789"
}
}Authentication Patterns
认证模式
| Pattern | Use Case | Header |
|---|---|---|
| Bearer Token | User authentication (JWT, OAuth2) | |
| API Key | Service-to-service, third-party integrations | |
| OAuth2 | Third-party user authorization | |
| Basic Auth | Simple internal services (over HTTPS only) | |
| 模式 | 适用场景 | 请求头 |
|---|---|---|
| Bearer令牌 | 用户认证(JWT、OAuth2) | |
| API密钥 | 服务间调用、第三方集成 | |
| OAuth2 | 第三方用户授权 | |
| 基础认证 | 简单内部服务(仅在HTTPS下使用) | |
Authentication Rules
认证规则
- Always use HTTPS in production
- Never pass tokens or keys in URL query params for GET requests (they appear in logs)
- API keys should be rotatable without downtime
- Return 401 for missing/invalid credentials, 403 for insufficient permissions
- Include header in 401 responses
WWW-Authenticate
- 生产环境必须使用HTTPS
- GET请求的令牌或密钥绝对不能放在URL查询参数中(会出现在日志中)
- API密钥应支持无停机轮换
- 凭证缺失/无效返回401,权限不足返回403
- 401响应中需包含头
WWW-Authenticate
Input Validation
输入验证
Validation Order
验证顺序
- Type validation - Is the JSON well-formed? Are types correct?
- Presence validation - Are all required fields present?
- Format validation - Do values match expected patterns (email, URL, UUID)?
- Range validation - Are numbers within bounds? Are strings within length limits?
- Business validation - Does the data make sense in context?
- 类型验证 - JSON格式是否正确?类型是否匹配?
- 必填项验证 - 所有必填字段是否存在?
- 格式验证 - 值是否符合预期模式(邮箱、URL、UUID)?
- 范围验证 - 数值是否在范围内?字符串长度是否符合限制?
- 业务规则验证 - 数据在业务场景下是否合理?
Return All Errors at Once
一次性返回所有错误
json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Multiple validation errors occurred.",
"details": [
{ "field": "email", "message": "Required field.", "code": "REQUIRED" },
{ "field": "age", "message": "Must be between 0 and 150.", "code": "OUT_OF_RANGE" },
{ "field": "name", "message": "Must be 1-100 characters.", "code": "INVALID_LENGTH" }
]
}
}Never return one error at a time forcing clients to resubmit repeatedly.
json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Multiple validation errors occurred.",
"details": [
{ "field": "email", "message": "Required field.", "code": "REQUIRED" },
{ "field": "age", "message": "Must be between 0 and 150.", "code": "OUT_OF_RANGE" },
{ "field": "name", "message": "Must be 1-100 characters.", "code": "INVALID_LENGTH" }
]
}
}绝对不能每次只返回一个错误,迫使客户端反复提交请求。
Filtering, Sorting, and Field Selection
过滤、排序与字段选择
Filtering
过滤
http
GET /api/v1/orders?status=pending&created_after=2025-01-01
GET /api/v1/products?category=electronics&min_price=100&max_price=500http
GET /api/v1/orders?status=pending&created_after=2025-01-01
GET /api/v1/products?category=electronics&min_price=100&max_price=500Sorting
排序
http
GET /api/v1/users?sort=created_at # Ascending (default)
GET /api/v1/users?sort=-created_at # Descending (prefix with -)
GET /api/v1/users?sort=name,-created_at # Multiple fieldshttp
GET /api/v1/users?sort=created_at # 升序(默认)
GET /api/v1/users?sort=-created_at # 降序(前缀加-)
GET /api/v1/users?sort=name,-created_at # 多字段排序Field Selection (Sparse Fieldsets)
字段选择(稀疏字段集)
http
GET /api/v1/users?fields=id,name,email
GET /api/v1/orders?fields=id,total,statushttp
GET /api/v1/users?fields=id,name,email
GET /api/v1/orders?fields=id,total,statusQuery Parameter Rules
查询参数规则
- Use consistent naming across all endpoints
- Document every supported filter, sort field, and selectable field
- Ignore unknown query parameters (do not error)
- Apply sensible defaults when parameters are omitted
- Validate and sanitize all query parameter values
- 所有接口使用统一的命名规范
- 文档化所有支持的过滤条件、排序字段和可选字段
- 忽略未知的查询参数(不返回错误)
- 参数缺失时使用合理默认值
- 验证并清理所有查询参数值