
你的 API 是契約,不是隨便寫寫的 endpoint
一句話總結:API 是前後端之間的合約,設計好了雙方各自開發互不干擾,設計爛了就是無止盡的聯調地獄。
結論先講:好的 API 看到 URL 就能猜出行為、看到錯誤就知道怎麼處理、看到文件就能直接串接。做不到這三點,你的 API 就只是一堆散亂的 endpoint。
你有沒有串過這種 API?
取得使用者列表是 GET /getUsers,建立訂單是 POST /create-order,查詢商品是 GET /fetch_products。三個 endpoint 三種命名風格,錯誤回應一個回 { message: "..." }、一個回 { error: "..." }、一個直接回字串。
恭喜,你剛走進了聯調地獄的大門。
API 設計混亂的代價不是「工程師覺得醜」,是前端每串一個 endpoint 都要猜格式、後端每改一次都要擔心前端會不會壞、新人接手的時候一臉問號。
RESTful 的核心:把一切當資源
REST 的思想很簡單——URL 代表資源的位置,HTTP 方法代表對資源的操作。
GET /users → 取得使用者列表
GET /users/42 → 取得 ID 42 的使用者
POST /users → 建立新使用者
PATCH /users/42 → 部分更新使用者
DELETE /users/42 → 刪除使用者
GET /users/42/orders → 取得該使用者的訂單
看到了嗎?URL 裡沒有動詞。不是 /getUsers、/createUser、/deleteUser。動作由 HTTP method 來表達,URL 只負責指出「操作的對象是誰」。
幾個命名原則:
- 用名詞不用動詞:
/users不是/getUsers - 用複數:
/users不是/user - 用 kebab-case:
/user-profiles不是/userProfiles - 巢狀不超過兩層:
/users/42/orders可以,/users/42/orders/7/items/3/reviews就太深了
HTTP 方法不是裝飾品
每個 HTTP 方法都有明確的語義,不是你高興用哪個就用哪個:
- GET:讀取,冪等,安全——不管你 GET 幾次,結果都一樣,不會改變任何東西
- POST:建立,不冪等——連續 POST 兩次可能建立兩筆資料
- PUT:完整取代——你得送完整的物件過去
- PATCH:部分更新——只送你要改的欄位
- DELETE:刪除,冪等——刪一次跟刪三次結果一樣
冪等性(Idempotency)不是學術名詞,它直接影響前端要不要做 retry 機制。GET、PUT、DELETE 可以安全重試,POST 不行。這不是你可以「看情況」的事。
狀態碼:跟前端溝通的語言
你有沒有遇過所有 API 都回 200,錯誤也回 200 只是 body 裡有個 success: false?那你的前端工程師一定恨你。
正確使用狀態碼:
成功系列:
200 OK:GET 成功、PATCH 成功201 Created:POST 建立新資源成功204 No Content:DELETE 成功(沒有 body 要回)
客戶端錯誤系列:
400 Bad Request:你送的格式有問題401 Unauthorized:你沒登入或 token 過期403 Forbidden:你登入了但沒權限404 Not Found:你找的東西不存在409 Conflict:重複建立422 Unprocessable Entity:格式對但業務邏輯不允許429 Too Many Requests:你打太快了
伺服器錯誤系列:
500:後端炸了502:上游服務掛了503:服務暫時不可用
401 跟 403 的差異很多人搞混:401 是「你是誰?」(沒認證),403 是「我知道你是誰,但你不行」(沒權限)。
分頁:不要一次吐幾萬筆
當資源量大的時候,分頁是必須的。兩種常見做法:
Offset-based(傳統):
GET /users?page=2&limit=20
好處是可以跳頁,壞處是資料即時異動時可能重複或漏掉。
Cursor-based(推薦用於即時資料流):
GET /users?cursor=eyJpZCI6MTAwfQ&limit=20
效能更好、不怕資料異動,但沒辦法跳到任意頁。
排序跟過濾用 query parameter:
GET /users?status=active&role=admin&sort=-created_at,name
- 代表降序,逗號分隔多重排序。
版本管理:從第一天就開始
API 一定會改,問題是你改了之後舊的 client 怎麼辦。
最推薦也最直觀的做法是 URL 路徑版本:/v1/users。為什麼?因為 API Gateway 層做路由分流最方便,debug 的時候看 URL 就知道是哪個版本,CDN 快取也不會有問題。
Header 版本 Accept: application/vnd.api+json;version=1 看起來很優雅,但實務上不容易 debug、CDN 快取困難,大部分團隊用不到。
統一錯誤格式:前端只需要一個 interceptor
所有錯誤回應長一樣,前端就可以用一個 interceptor 處理所有錯誤。格式建議:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "輸入資料驗證失敗",
"details": [
{ "field": "email", "message": "email 格式不正確" },
{ "field": "password", "message": "密碼長度至少 8 個字元" }
],
"requestId": "req_abc123"
}
}requestId 很重要——前端回報問題時,後端可以用這個 ID 直接從日誌裡撈到對應的 request,不用在茫茫 log 海裡大海撈針。
這篇的重點回顧
API 設計的基礎是一致性:命名統一、方法語義正確、狀態碼不亂用、錯誤格式一致。下一篇我們聊認證機制——JWT、OAuth、API Key 的選用時機跟安全注意事項。
系列文章:
- 你在這裡 → API 設計(一):RESTful 基礎
- API 設計(二):JWT、OAuth 與認證機制
- API 設計(三):實戰程式碼範例
- API 設計(四):API 文件化與常見陷阱
延伸閱讀:
「一致的 API 設計就像交通規則——遵守的時候感覺多此一舉,不遵守的時候大家一起車禍。」