寫一個能跑的 API 很快,但設計一個「會被別人用很多年、而且你還得持續維護」的 API,是完全不同的功夫。一旦有人開始依賴你的 API,你的每一個設計決定都變成要長期背負的承諾。這篇講我設計 API 時,最在意的幾件事:版本、錯誤、分頁,以及一致性。
一致性比聰明重要
我設計 API 的第一原則:一致性勝過個別的巧思。命名風格、日期格式、分頁方式、錯誤結構,整套 API 要長得一樣。
當使用者學會了你一個端點怎麼用,他應該能合理猜到其他端點怎麼用。每個端點都自成一格、各有各的巧妙,對使用者是惡夢。無聊的一致,遠勝聰明的混亂。
版本,從第一天就要想
只要 API 會對外、會被別人依賴,破壞性的改動就會害到對方。你不能哪天默默把一個欄位改名、把回傳結構改掉,那會讓所有在用的人在正式環境炸掉。
所以版本策略從一開始就要有。把版本放進路徑(像 /v1/)是最直觀的做法。原則是:
- 在同一個版本內,只做「往後相容」的改動:加欄位可以,改名、刪欄位、改型別不行
- 真的要破壞性改動,開新版本,讓舊版本還能用一段時間,給使用者時間遷移
- 別偷偷改。API 的契約是一種承諾
錯誤要可預測、可程式化處理
很多 API 的錯誤處理很隨便,出錯就回一個 500 加一段給人看的文字。問題是呼叫你的是程式,不是人。程式需要的是能判斷、能分流的錯誤。
我的做法:
- 用對 HTTP 狀態碼:400 是你(呼叫端)的請求有問題,401/403 是認證授權,404 是找不到,500 是我(伺服器)的錯。不要全部都回 200 然後在 body 裡藏錯誤
- 回傳結構化的錯誤:一個穩定的錯誤碼(讓程式判斷)、一段訊息(給開發者看)、必要時附上是哪個欄位錯。錯誤碼要穩定,因為對方會用它來寫邏輯
- 不要把堆疊細節、資料庫錯誤吐給對方,那是資安問題
分頁:別把全世界一次回給人
「回傳所有訂單」這種端點,資料一多就會慢、會把記憶體吃爆、會把網路塞滿。任何「列表」端點從一開始就要分頁,別等資料長大才補。
兩種常見方式,各有適用:
- 位移分頁(offset/limit):直觀、可以跳頁,但資料量很大、或邊翻邊有新資料插入時,會有效能與重複/遺漏的問題
- 游標分頁(cursor):用「上一頁最後一筆的位置」當作下一頁的起點。不能任意跳頁,但在大資料量、即時變動的清單上穩定又有效率
我的選擇邏輯:要給人點頁碼的後台,用 offset 夠了;資料量大、會一直變動的(像動態流、交易紀錄),用 cursor。
其他我會堅持的小事
- 日期一律用標準格式(ISO 8601)並講清楚時區,別每個端點各回各的格式
- 金額用字串或整數傳,別用浮點數(JavaScript 的數字是 float,會掉精度)
- 想清楚哪些欄位可能是 null,並在文件講明白,別讓對方一直踩 undefined
- 文件要跟著 API 走。沒文件、或文件過時的 API,使用體驗很差
小結
設計一個會被長期使用的 API,我最在意的:
- 一致性勝過個別的聰明
- 版本從第一天就規劃,同版本只做往後相容的改動,破壞性改動開新版
- 錯誤要可預測、可程式化處理:對的狀態碼、穩定的錯誤碼、結構化的回應
- 列表一律分頁,依場景選 offset 或 cursor
- 日期、金額、null、文件這些小事都要一致與嚴謹
API 是一種承諾。設計時多想一步「別人會怎麼依賴它、我以後要怎麼改它」,能幫你和所有使用者省下大量未來的痛苦。
#API 設計#REST#版本控制#分頁#後端