JSON API 設計模式:打造更好的 REST API
設計良好的 API 是使用的樂趣。設計不良的則會產生錯誤、挫折和技術債。本指南涵蓋經過實戰驗證的 JSON REST API 設計模式,使其一致、可探索且易於維護。
資源命名
資源是名詞,而非動詞。對集合使用複數名詞,透過 ID 存取單一資源:
GET /api/users # List users
POST /api/users # Create a user
GET /api/users/123 # Get user 123
PUT /api/users/123 # Update user 123
DELETE /api/users/123 # Delete user 123
命名慣例:
- 使用小寫加連字號:
/api/blog-posts(而非blogPosts或blog_posts) - 巢狀相關資源:
/api/users/123/orders - 巢狀限制在 2 層:
/api/users/123/orders/456(不要更深) - 避免在 URL 中使用動詞:
/api/users/123/activate對於無法對應到 CRUD 的操作是可接受的
回應封裝
將回應包裝在一致的封裝中:
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
},
"meta": {
"requestId": "req_abc123"
}
}
集合的情況:
{
"data": [
{ "id": "123", "name": "Alice" },
{ "id": "456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20
}
}
使用我們的 JSON 驗證器驗證你的 API 回應,確保它們符合你的結構描述。
分頁
三種常見方法:
偏移量分頁(最簡單)
GET /api/users?page=2&per_page=20
{
"data": [...],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
}
}
優點:簡單,支援跳轉到任意頁面。缺點:在並行插入/刪除時結果不一致。
游標分頁(最可靠)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"meta": {
"hasNext": true,
"nextCursor": "eyJpZCI6MTQzfQ"
}
}
優點:在並行變更時保持一致,大型資料集效能佳。缺點:無法跳轉到任意頁面。
鍵集分頁(效能最佳)
GET /api/users?after_id=123&limit=20
使用最後一筆項目的 ID(或其他排序欄位)來取得下一頁。類似游標分頁,但參數更透明。
建議:即時動態消息和大型資料集使用游標分頁。需要頁面跳轉的管理後台使用偏移量分頁。
篩選與排序
篩選
GET /api/users?status=active&role=admin
GET /api/users?created_after=2024-01-01
GET /api/users?search=alice
對於複雜的篩選,考慮使用專用的查詢參數:
GET /api/users?filter[status]=active&filter[role]=admin
排序
GET /api/users?sort=name # Ascending
GET /api/users?sort=-created_at # Descending (prefix with -)
GET /api/users?sort=-created_at,name # Multiple fields
錯誤處理
一致的錯誤回應對 API 的可用性至關重要:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"value": "not-an-email"
},
{
"field": "age",
"message": "Must be between 0 and 150",
"value": -5
}
]
},
"meta": {
"requestId": "req_abc123"
}
}
應使用的 HTTP 狀態碼:
| 狀態碼 | 含義 | 使用時機 |
|---|---|---|
| 200 | OK | 成功的 GET、PUT |
| 201 | Created | 成功的 POST |
| 204 | No Content | 成功的 DELETE |
| 400 | Bad Request | 驗證錯誤 |
| 401 | Unauthorized | 缺少/無效的認證 |
| 403 | Forbidden | 認證有效但權限不足 |
| 404 | Not Found | 資源不存在 |
| 409 | Conflict | 重複資源、版本衝突 |
| 422 | Unprocessable | 語義上無效 |
| 429 | Too Many Requests | 超出速率限制 |
| 500 | Internal Error | 非預期的伺服器錯誤 |
版本控制
三種方法:
URL 路徑(建議)
GET /api/v1/users
GET /api/v2/users
優點:明確、容易路由、容易測試。
標頭方式
GET /api/users
Accept: application/vnd.myapi.v2+json
優點:乾淨的 URL。缺點:較難測試、較不容易發現。
查詢參數
GET /api/users?version=2
優點:容易測試。缺點:可選參數的語義問題。
建議:URL 路徑版本控制是最實際的選擇。它明確、可快取,且適用於所有 HTTP 工具。
日期與時間處理
一律使用 ISO 8601 格式的 UTC:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33Z",
"expiresAt": "2024-12-31T23:59:59Z"
}
永遠不要在 API 回應中使用 Unix 時間戳——它們含糊不清(秒 vs 毫秒)且不易閱讀。有關時間戳處理的更多資訊,請參閱我們的 Unix 時間戳指南。
速率限制
在回應標頭中傳達速率限制:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1705312800
Retry-After: 30
當超出限制時返回 429 Too Many Requests,並附上清楚的錯誤訊息和 Retry-After 標頭。
HATEOAS(可選但強大)
包含相關資源和操作的連結:
{
"data": {
"id": "123",
"name": "Alice",
"links": {
"self": "/api/users/123",
"orders": "/api/users/123/orders",
"avatar": "/api/users/123/avatar"
}
}
}
這使你的 API 具有自我說明的能力,並減少客戶端的 URL 建構工作。
常見問題
我應該使用 JSON:API 規範還是設計自己的格式?
JSON:API 規範(jsonapi.org)提供了一個全面的標準,但對於簡單的 API 來說可能過於冗長。對於大多數專案,按照本指南中的模式設計較簡單的自訂格式更為實用。如果你需要自動化客戶端函式庫和嚴格的標準,則使用 JSON:API。
我應該如何處理部分更新(PATCH vs PUT)?
使用 PUT 進行完整的資源替換(客戶端發送所有欄位)。使用 PATCH 進行部分更新(客戶端僅發送變更的欄位)。使用 JSON Merge Patch(RFC 7396)的 PATCH 是最簡單的方法:發送一個僅包含要更新欄位的 JSON 物件,使用 null 來移除欄位。
相關資源
- JSON 格式化工具 — 格式化 API 回應以提高可讀性
- JSON Schema 驗證指南 — 使用 JSON Schema 驗證 API 資料
- JWT 令牌詳解 — 使用 JSON Web Token 保護你的 API