C Talk+ #15:就講 Web API 設計,讓 API 從「玄學」到「可預期」

Web API 設計

在產品開發的過程中,你是否曾遇過這樣的場景:產品經理(PM)提出了一個看似簡單的需求,工程師卻眉頭深鎖地說「這 API 結構要大改」;或者當功能上線、用戶量激增後,原本順暢的頁面突然變得卡頓不堪。

這些關於溝通與效能的摩擦,往往源自於我們對 Web API 設計的理解不一致。

在 CMoney 第 15 場 C Talk+ 講座中,我們邀請到 CMoney 後端 Group Lead Martin 以「讓 API 從『玄學』到『可預期』」為題,帶領大家進入一場從 RFC 標準到現代化架構的重構之旅。

為什麼 API 不該是「玄學」?

什麼叫做「可預期」?Martin 用了一個非常直白的隱喻:今天出門看著窗外烏雲密佈,如果你沒帶傘,你「預期」自己等一下會淋濕;如果你帶了傘,你則「預期」自己能全身而退,這就是預期心理。

在軟體開發的世界裡,API 就像是不同系統、不同團隊之間的通訊合約。一個好的設計,應該讓串接它的前端工程師、甚至是規劃需求的 PM,都能預期呼叫後的結果。然而,當設計缺乏規範時,API 就會變成一種「玄學」,溝通時就如同雞同鴨講,例如:PM 覺得只是多傳一個參數,工程師卻說這不符合語意;或者有時候只是改動了一個小功能,原本串好的 API 就莫名其妙壞掉了,這背後的問題在於 API 的改動並非團隊的「共識」。

API 的設計本質上是「資源導向(Resource-oriented)」的藝術。一個良好的 API 路徑應該像地圖一樣清晰:當我們要找某個社團裡的成員權限,路徑自然會呈現出「社團 > 成員 > 權限」的層級感,這種巢狀式的路徑設計,讓任何人一眼就能看懂資源之間的邏輯關係。

 

語意與規則:API 的行為準則

取得 Token 到底該用 GET 還是 POST?

演講的第一個高潮點在於對「語意(Semantics)」的深度辯論。雖然我們口頭上說「拿(GET)一個 Token」,但實際上,在伺服器後端,這個動作往往伴隨著創建 Session、 Refresh Token 「寫入」伺服器等流程。根據 RFC 標準,GET 必須是安全(Safe)且冪等(Idempotent)的,意即不應對伺服器資源產生副作用,而 Token 申請涉及狀態變更,因此 POST 才是唯一符合預期的選擇。

冪等性(Idempotency)

Martin 用「穿外套」來比喻:如果你穿上一件外套,再穿一次、穿三次,最終結果你身上還是只有一件外套,這就是「冪等」——多次操作的結果與一次相同。在 API 設計中,Delete 動作就具備這種特性。

當我們連續刪除同一個資源時,第一次成功後會回傳 204(No Content),代表資源已處理;第二次再刪除,雖然資源已經不在了,但對於伺服器狀態來說,不論呼叫一次或多次 DELETE,該資源「被抹除」的最終狀態是一致的。

實務上我們可以跟前端協調,第二次是否改回 404(Not Found),讓前端工程師能清楚辨識該資源是否已經「預期地」被刪除,從而優化使用者體驗。這種細微的狀態碼溝通,正是減少 Bug 的關鍵。

 

效能與擴展:決定產品天花板的細節

當產品規模從 MVP 進化到百萬用戶時,效能往往卡在 API 的細節裡。為什麼產品剛上線時沒事,用戶突破十萬後就開始變慢?Martin 深度剖析了 Offset-based 與 Cursor-based 分頁策略的差異。

大多數傳統系統習慣使用 OFFSET 寫法(例如 LIMIT 10 OFFSET 100000),也就是告訴資料庫「跳過前 100 筆,給我後 10 筆」。但在數據量龐大時,伺服器必須掃描過前面所有的數據才能找到起點,這就像翻書要從第一頁翻到第一百萬頁一樣慢。Martin 建議,當資料量超過十萬甚至百萬級距時,面對高併發情境更傾向於使用「Cursor-based Pagination(游標式分頁)」,透過傳遞一個唯一識別碼(如 Encoded ID 或 Timestamp),將查詢轉化為具有索引支持的 O(log N) 或 O(1),它像是一個精準的書籤,不僅提升了回應速度,更避免了在資料頻繁增刪時產生的「跳頁」或「重複顯示」問題。

此外,針對「GET 是否能帶 Request Body」的爭議,Martin 從 Web 基礎設施的角度給出具體分析。雖然技術上可行,但多數代理伺服器(Proxy)、快取節點或框架預設不解析 GET 的 Payload,這會導致請求在傳輸過程中遺失數據。若搜尋參數過於複雜(例如多條件篩選),他建議採用「查詢資源化」的模式:先以 POST 建立一個暫時的「搜尋任務 ID」,再透過 GET 搭配該 ID 取得結果。這不僅解決了參數過多的問題,也讓搜尋結果能被有效 Cache 的優點。

在資料結構的選擇上,他也建議優先使用 JSON Object 而非單純的 JSON Array。以「回傳社團會員編號」為例,若只回傳一串數字陣列,未來想新增 total_count 或 next_cursor 等 MetaData 時,就會被迫破壞原有的結構;但若一開始就包裝成 Object,就能在不影響舊功能的前提下,靈活地擴充資訊。

 

標準化與生態系:看科技大廠怎麼做

在 API 的異常處理段落,Martin 提出一個警示:「永遠不要假裝沒事」。有些開發者為了讓前端「不報錯」,會在出錯時仍回傳 HTTP 200 搭配自定義的 error: true,這會導致所有自動化的監控指標(如 Error Rate)與警報系統失效。

為了提升設計品質,導覽 Gmail、Facebook 與 Apple Store Connect 的 API 後,發現這些大廠的設計並非遙不可及,而是展現了極致的規範化。例如:Apple 會提供完整的 OpenAPI 規範文件,前端工程師只要將文件導入工具,便可大幅減少了手動串接的工時與錯誤率。

 

CMoney 實戰:從直播服務到百萬級分頁的進化

在 CMoney 內部,這些理論並非紙上談兵,而是真實發生在我們的產品演進中。多數開發者停留在 Level 2(HTTP Verbs + Resources),但要達到 Level 3 的 HATEOAS(Hypermedia as the Engine of Application State)則需要更高層次的思考。

隨著 CMoney 的產品(如:籌碼 K 線)用戶與資料量爆發性成長,為了確保用戶在刷分頁、看討論區時不會感受到毫秒級的延遲,研發團隊逐步將系統重構成高效率的 Cursor-based 架構。

此外,針對一些耗時的任務(如大型檔案上傳或複雜數據運算),我們導入了異步處理機制。利用 Redis 作為中間層,當前端發送請求後,後端會先回傳 202 Accepted,代表「我收到任務了,正在處理中」,而前端此時可以透過輪詢(Polling)該連結來確認處理進度。不僅釋放了後端連線資源,也讓用戶能即時獲得「進度中」的反饋,而非卡在 Loading 畫面。。

 

結語:API 是一場關於「共識」的對話

API 設計絕非孤島。它向上承接產品邏輯,向下決定系統效能。

透過 OpenAPI 規範文件的自動化產出,我們可以讓前端與後端在開發前期就建立 Mock Server,實現並行開發,讓團隊溝通建立在可驗證的標準之上。API 是一場關於「共識」的對話,當我們能從 RFC 標準中尋找解答,原本主觀的爭議就會轉化為對卓越架構的追求。

下次當你在開需求、或是討論 API 規格時,不妨試著從「可預期性」的角度出發:這個路徑夠直覺嗎?是否考慮了百萬級數據下的效能表現?當我們能把這些細節做對,API 就不再是冷冰冰的數據交換,而是支撐起強大技術生態的堅實骨幹。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

返回頂端