api-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

API Design

API设计

RESTful Resource Naming

RESTful资源命名

Conventions

约定规则

RuleGoodBad
Use plural nouns
/users
/user
,
/getUsers
Use nouns, not verbs
POST /orders
POST /createOrder
Nest for relationships
/users/123/orders
/getUserOrders?userId=123
Use kebab-case
/user-profiles
/userProfiles
,
/user_profiles
Keep URLs shallow (max 3)
/users/123/orders
/users/123/orders/456/items/789
Use query params for filters
/orders?status=pending
/orders/pending
Collection + resource IDs
/users/123
/user?id=123
规则示例(规范)示例(不规范)
使用复数名词
/users
/user
,
/getUsers
使用名词而非动词
POST /orders
POST /createOrder
嵌套表示资源关系
/users/123/orders
/getUserOrders?userId=123
使用短横线分隔命名(kebab-case)
/user-profiles
/userProfiles
,
/user_profiles
保持URL层级简洁(最多3层)
/users/123/orders
/users/123/orders/456/items/789
使用查询参数实现过滤
/orders?status=pending
/orders/pending
集合+资源ID格式
/users/123
/user?id=123

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 collection
https://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方法语义

MethodPurposeIdempotentSafeRequest BodySuccess Code
GET
Retrieve resource(s)YesYesNo200
POST
Create a resourceNoNoYes201
PUT
Full replacementYesNoYes200
PATCH
Partial updateNo*NoYes200
DELETE
Remove a resourceYesNoNo204
*
PATCH
can be made idempotent with proper implementation but is not guaranteed by the spec.
方法用途幂等性安全性请求体成功状态码
GET
获取资源(单个或多个)200
POST
创建新资源201
PUT
完整替换资源200
PATCH
部分更新资源否*200
DELETE
删除资源204
*通过合理实现可让
PATCH
具备幂等性,但规范本身不做保证。

Method Usage Rules

方法使用规则

  • GET
    must never modify server state
  • POST
    is the only method for creating new resources
  • PUT
    sends the complete resource; omitted fields are set to defaults or null
  • PATCH
    sends only the fields to change; omitted fields remain unchanged
  • DELETE
    returns 204 on success, and is a no-op if the resource already does not exist
  • GET
    绝对不能修改服务器状态
  • POST
    是创建新资源的唯一方法
  • PUT
    需发送完整资源对象;未传递的字段会被设为默认值或null
  • PATCH
    仅发送需要修改的字段;未传递的字段保持不变
  • DELETE
    成功时返回204;若资源已不存在,执行无操作即可

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/123
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/123

Status Code Guide

状态码指南

Success Codes

成功状态码

CodeNameWhen to Use
200
OKSuccessful GET, PUT, PATCH, or DELETE with body
201
CreatedSuccessful POST that created a resource
204
No ContentSuccessful DELETE or PUT with no response body
状态码名称使用场景
200
OKGET、PUT、PATCH或DELETE请求成功且返回响应体
201
CreatedPOST请求成功且创建了新资源
204
No ContentDELETE或PUT请求成功且无响应体返回

Client Error Codes

客户端错误状态码

CodeNameWhen to Use
400
Bad RequestMalformed JSON, invalid syntax
401
UnauthorizedMissing or invalid authentication credentials
403
ForbiddenAuthenticated but not authorized for this action
404
Not FoundResource does not exist at this URL
409
ConflictResource state conflict (duplicate, version mismatch)
422
Unprocessable EntityValid JSON but fails business validation
429
Too Many RequestsRate limit exceeded
状态码名称使用场景
400
Bad RequestJSON格式错误、语法无效
401
Unauthorized缺少或无效的认证凭证
403
Forbidden已认证但无此操作权限
404
Not Found请求的资源在该URL不存在
409
Conflict资源状态冲突(重复、版本不匹配)
422
Unprocessable EntityJSON格式合法但不符合业务规则
429
Too Many Requests超出请求频率限制

Server Error Codes

服务器错误状态码

CodeNameWhen to Use
500
Internal Server ErrorUnexpected server failure
503
Service UnavailableServer is down for maintenance or overloaded
状态码名称使用场景
500
Internal Server Error服务器意外故障
503
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 Conflict

Structured 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
    code
    (uppercase snake_case)
  • Always include a human-readable
    message
  • Include
    details
    array for field-level validation errors
  • Include
    request_id
    for traceability
  • 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 CodeHTTP StatusDescription
VALIDATION_FAILED
422One or more fields are invalid
RESOURCE_NOT_FOUND
404Requested resource does not exist
AUTHENTICATION_REQUIRED
401No valid credentials provided
PERMISSION_DENIED
403Insufficient permissions
CONFLICT
409Resource state conflict
RATE_LIMIT_EXCEEDED
429Too many requests
INTERNAL_ERROR
500Unexpected server error
SERVICE_UNAVAILABLE
503Dependency or server is down
错误码HTTP状态码描述
VALIDATION_FAILED
422一个或多个字段无效
RESOURCE_NOT_FOUND
404请求的资源不存在
AUTHENTICATION_REQUIRED
401未提供有效凭证
PERMISSION_DENIED
403权限不足
CONFLICT
409资源状态冲突
RATE_LIMIT_EXCEEDED
429请求频率超限
INTERNAL_ERROR
500服务器意外错误
SERVICE_UNAVAILABLE
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=eyJpZCI6MTAwfQ
Response:
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
    has_more
    boolean
适用于实时数据、大型数据集和结果一致性要求高的场景。
请求示例:
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=25
Response:
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

场景选择

ApproachUse When
Cursor-basedReal-time data, infinite scroll, large datasets
Offset-basedAdmin panels, reports where total count is needed
分页方式适用场景
基于游标实时数据、无限滚动、大型数据集
基于偏移量管理面板、需要总计数的报表场景

Pagination Rules

分页规则

  • Default
    limit
    or
    per_page
    to a sensible value (e.g., 20)
  • Enforce a maximum limit (e.g., 100) to prevent abuse
  • Always include pagination metadata in the response
  • Return an empty
    data
    array (not null) when no results match
  • limit
    per_page
    设置合理默认值(如20)
  • 强制设置最大限制(如100)防止滥用
  • 响应中始终包含分页元数据
  • 无匹配结果时返回空
    data
    数组(而非null)

Versioning Strategies

版本控制策略

StrategyExampleProsCons
URL path
/api/v1/users
Explicit, easy to routeURL changes on version bump
Header
Accept: application/vnd.api+json; version=1
Clean URLsHidden, harder to test
Query param
/api/users?version=1
Easy to addNot standard, clutters query
策略示例优点缺点
URL路径版本
/api/v1/users
明确、易于路由版本升级时URL会变更
请求头版本
Accept: application/vnd.api+json; version=1
URL简洁不直观、测试难度高
查询参数版本
/api/users?version=1
易于添加非标准、污染查询参数

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
    Sunset
    header to communicate deprecation date
  • 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:
HeaderValue
X-RateLimit-Limit
Maximum requests per window
X-RateLimit-Remaining
Requests remaining in current window
X-RateLimit-Reset
Unix timestamp when the window resets
Retry-After
Seconds to wait (on 429 responses only)
所有响应需包含以下头信息:
响应头
X-RateLimit-Limit
时间窗口内的最大请求数
X-RateLimit-Remaining
当前时间窗口剩余请求数
X-RateLimit-Reset
时间窗口重置的Unix时间戳
Retry-After
需等待的秒数(仅在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

认证模式

PatternUse CaseHeader
Bearer TokenUser authentication (JWT, OAuth2)
Authorization: Bearer <token>
API KeyService-to-service, third-party integrations
X-API-Key: <key>
or query param
OAuth2Third-party user authorization
Authorization: Bearer <access_token>
Basic AuthSimple internal services (over HTTPS only)
Authorization: Basic <base64>
模式适用场景请求头
Bearer令牌用户认证(JWT、OAuth2)
Authorization: Bearer <token>
API密钥服务间调用、第三方集成
X-API-Key: <key>
或查询参数
OAuth2第三方用户授权
Authorization: Bearer <access_token>
基础认证简单内部服务(仅在HTTPS下使用)
Authorization: Basic <base64>

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
    WWW-Authenticate
    header in 401 responses
  • 生产环境必须使用HTTPS
  • GET请求的令牌或密钥绝对不能放在URL查询参数中(会出现在日志中)
  • API密钥应支持无停机轮换
  • 凭证缺失/无效返回401,权限不足返回403
  • 401响应中需包含
    WWW-Authenticate

Input Validation

输入验证

Validation Order

验证顺序

  1. Type validation - Is the JSON well-formed? Are types correct?
  2. Presence validation - Are all required fields present?
  3. Format validation - Do values match expected patterns (email, URL, UUID)?
  4. Range validation - Are numbers within bounds? Are strings within length limits?
  5. Business validation - Does the data make sense in context?
  1. 类型验证 - JSON格式是否正确?类型是否匹配?
  2. 必填项验证 - 所有必填字段是否存在?
  3. 格式验证 - 值是否符合预期模式(邮箱、URL、UUID)?
  4. 范围验证 - 数值是否在范围内?字符串长度是否符合限制?
  5. 业务规则验证 - 数据在业务场景下是否合理?

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=500
http
GET /api/v1/orders?status=pending&created_after=2025-01-01
GET /api/v1/products?category=electronics&min_price=100&max_price=500

Sorting

排序

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 fields
http
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,status
http
GET /api/v1/users?fields=id,name,email
GET /api/v1/orders?fields=id,total,status

Query 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
  • 所有接口使用统一的命名规范
  • 文档化所有支持的过滤条件、排序字段和可选字段
  • 忽略未知的查询参数(不返回错误)
  • 参数缺失时使用合理默认值
  • 验证并清理所有查询参数值