
「我這個功能該用 REST 還是 GraphQL?該用 WebSocket 還是 SSE?要不要開 webhook?」
這個問題很多人問,但網路上找到的答案通常分散在 5-6 篇文章裡——每篇講一種協定,沒有放在一起比較的「全景」。讀完還是不知道自己的場景該選哪個。
這篇就是來解這個問題的。6 種協定放同一頁、同一張比較表、同一個決策樹。讀完你會知道:你的 use case 該選哪個、為什麼。
一張表先講結論
| 協定 | 方向 | 連線型態 | 格式 | 瀏覽器原生支援 | Streaming | 適合場景 |
|---|---|---|---|---|---|---|
| REST | Client → Server | 一問一答(連線預設重用,keep-alive) | JSON | ✅ | ❌ | CRUD / 對外 API / 公開 API |
| GraphQL | Client → Server | 短連接 | JSON | ✅ | ⚠️ subscription 需 WS | 前端要彈性查詢 / 多端 client / over-fetching 嚴重 |
| gRPC | 雙向 | 長連接 HTTP/2 | Protobuf | ❌(要 grpc-web 轉) | ✅ 雙向 streaming | 微服務之間 / 高效能內部通訊 |
| WebSocket | 雙向 | 長連接 TCP | 自訂 / JSON | ✅ | ✅ 雙向 | 聊天 / 協作編輯 / 即時遊戲 / 競標 |
| SSE | Server → Client | 長連接 HTTP | text/event-stream | ✅ | ✅ 單向 | 通知推送 / 進度條 / Dashboard / AI chat streaming |
| Webhook | Server → 你的 server | 對方主動打你 | JSON | N/A(不是瀏覽器用的) | ❌ | 第三方事件通知 / 異步整合 |
每一行接下來會展開講「什麼時候用 / 不該用」。
REST:絕大多數場景的預設選擇
特性:HTTP 動詞(GET / POST / PUT / DELETE)+ 資源 URL + JSON。
該用的場景:
- 對外公開 API(給第三方接的)
- CRUD 為主的後台 API
- 工程師團隊規模 < 50 人
- 你還沒有特殊需求
不該用的場景:
- 前端需要查很彈性的資料形狀——應該選 GraphQL(避免 N+1 個 endpoint)
- 微服務之間高頻呼叫——應該選 gRPC(JSON 太肥、HTTP/1 太慢)
- 需要 server 主動推資料——應該選 SSE 或 WebSocket
REST 是「你不知道該用什麼的時候,先用 REST」。它的成熟度、debug 難度、工具鏈支援都是其他協定難匹敵的。
GraphQL:前端要彈性查詢
特性:單一 endpoint、用 query language 描述要拿什麼欄位。
該用的場景:
- 多端 client(web / iOS / Android)但每端要的欄位不同
- 後端 over-fetching / under-fetching 嚴重(例如要 user 資料還要回他全部 order list)
- 前端工程師多過後端、希望前端能主導查詢結構
不該用的場景:
- 公開 API(GraphQL 的 schema introspection 是攻擊面,且 query complexity 不好限制)
- 服務間通訊(過於 chatty + 多了一層 schema 維護成本)
- 團隊小、後端不熟 GraphQL(比 REST 多很多運維坑)
特別注意:GraphQL 的 N+1 問題沒解 dataloader 會炸。Subscription 需要 WebSocket 支撐。
gRPC:服務間通訊的高效率選擇
特性:Protobuf 二進位編碼 + HTTP/2 + 雙向 streaming。
該用的場景:
- 微服務之間高頻呼叫(Protobuf 訊息比 JSON 小;第 22 篇的序列化格式比較是小約 50%、快約 30%,端到端延遲要看條件,見下方實驗佐證)
- 需要強型別 schema 約束
- 跨語言服務間調用(Go ↔ Python ↔ Java 統一介面)
不該用的場景:
- 瀏覽器直接打——gRPC 不能在 browser 原生跑,要走 grpc-web 轉一層(增加複雜度)
- 服務數少(< 5 個)——gRPC 的學習成本大過效能 leverage
- debug / 抓封包頻繁——Protobuf 不是人讀的,比 JSON 難 debug
實務做法:對外用 REST / GraphQL,對內用 gRPC——這是大多數團隊的最佳組合。
WebSocket:雙向即時通訊
特性:HTTP upgrade 後升級成 TCP 雙向連線。
該用的場景:
- 真正雙向:聊天室、協作編輯(Figma / Google Docs)、即時遊戲、競標
- 客戶端要主動 push 訊息給 server(不只接收)
不該用的場景:
- 單向 server → client 推——應該選 SSE(更輕、HTTP 原生、自動重連)
- 大多數通知 / 狀態推送——SSE 夠用,不需要 WebSocket 的雙向能力
- 需要透過 corporate proxy / firewall(WebSocket 比 HTTP/SSE 容易被擋)
WebSocket 常常被過度使用——很多人「需要即時推送」就上 WebSocket,但其實 SSE 在 90% 場景夠且更簡單。
SSE(Server-Sent Events):單向推送的最佳解
特性:HTTP 長連接、text/event-stream 格式、瀏覽器自動重連。
該用的場景:
- 訂單狀態推送(追蹤頁進度條)
- Dashboard 即時更新(股票、監控)
- 通知推送(信件、訊息提醒)
- AI chat streaming(ChatGPT / Claude 介面就是 SSE)
- 長任務進度回報
不該用的場景:
- 雙向溝通(用 WebSocket)
- 需要 binary 資料(SSE 純文字)
SSE 是 2024+ 被嚴重低估的協定。AI streaming 的興起讓它再度被重視——你寫 LLM-based 應用幾乎一定會用到。
Webhook:當第三方要主動通知你
特性:對方的 server 主動打你的 endpoint,攜帶事件 payload。
該用的場景:
- 第三方服務通知你某個事件發生(Stripe 付款成功、GitHub PR merged、Slack 訊息送達)
- 你不能也不該 polling——對方 API rate limit、polling 頻率不夠即時、對方不希望你一直敲
不該用的場景:
- 你想「主動拿資料」的場景——這是 API call,不是 webhook
- 內部服務之間(用 message queue / event bus 比 webhook 更可靠)
實作必備:HMAC 簽名驗證、立刻回 200 + 把工作丟 queue、idempotent 處理(同一個事件可能重複收到)。詳見 44-webhook-design。
Webhook vs API:Push 跟 Pull 的根本差異
很多新手搞不清楚 webhook 跟 API 的差別。本質是方向相反:
| 維度 | API call | Webhook |
|---|---|---|
| 主動方 | 你 | 對方 |
| 觸發 | 你決定何時打 | 事件發生時對方自動打你 |
| 即時性 | 看你 polling 頻率 | 接近即時 |
| 對方 server 負擔 | 高(你一直敲) | 低(事件驅動) |
| 你需要 | client 程式碼 | server endpoint + 公開可達 |
該 polling API 的場景:
- 你的 server 不能對外公開(沒固定 IP / 在公司內網)
- 對方沒提供 webhook
- 事件頻率高到 webhook 反而塞爆
該用 webhook 的場景:
- 對方提供且你能接(有公開 endpoint)
- 事件相對稀疏(不是每秒幾百個)
- 你想要接近即時的反應,不想付 polling 成本
混合做法很常見——webhook 通知 + API 拉取詳細資料。例如 Stripe webhook 告訴你「付款成功」,你再用 API 拉付款明細。
「我該選哪個」決策樹
你要做什麼?
├─ 提供公開 API 給第三方接
│ └─ REST(成熟度最高,第三方最不會抱怨)
│
├─ 後端 API 給自家 web/app 用
│ ├─ 多端 client + 欄位需求差異大 → GraphQL
│ └─ 標準 CRUD → REST
│
├─ 微服務之間互打
│ ├─ 高頻、要強型別、要效能 → gRPC
│ └─ 服務少(< 5 個)→ REST 內部用就好
│
├─ 即時通訊
│ ├─ 雙向(聊天 / 協作 / 遊戲)→ WebSocket
│ └─ 單向(推播 / 進度 / streaming) → SSE
│
└─ 第三方事件通知
├─ 對方主動通知 → Webhook
└─ 你主動拉 → REST polling90% 的選型可以套這棵樹解決。剩下 10% 是邊界 case(例如 polling 跟 webhook 混合用、SSE 跟 WebSocket 並存),那是 case-by-case 的工程判斷。
實驗佐證(2026-10,protocol-lab)
後來在外送平台實驗場把這幾個協定各放在真實位置跑過一次(一張訂單的一生、七組對照實驗),有幾處要補條件:
- REST 的連線:Node 的 fetch 預設 keep-alive,同一位外送員每秒一個 POST、50 個請求只開了 5 條連線——「每次重連」不成立,表格已改
- Protobuf「小 50%、快 30%」:這組數字來自第 22 篇的序列化格式比較(格式本身)。端到端呼叫是另一回事:本機各呼叫 1000 次,大小對得上(回應是 JSON 的 41%),延遲中位數 JSON 反而略低 0.009 ms——上面已改成分開講
- SSE 的自動重連:瀏覽器會自動重連並帶
Last-Event-ID,但補收要伺服器配合:每則帶id、重連時從那之後補送。HTTP/1.1 下瀏覽器對同一網域的連線數有上限(MDN 寫 6 條),開很多分頁會佔滿;HTTP/2 沒有這個問題 - Webhook:除了 HMAC、立刻回 200、冪等,還要防重放(簽章帶時間戳、超過 5 分鐘拒收)、用原始 body 驗簽、4xx 不重送/5xx 重送
- 沒有公開網址:不只 polling 一條路。長輪詢在同樣 30 秒、5 張單裡只打 5 次請求、平均 6 ms 收到;每 2 秒短輪詢要 15 次、平均晚 1113 ms
- 這張表沒列的:MQTT(行動裝置大量小訊息)、AMQP(內部非同步、下游掛了不能丟)、Kafka(事件要保留、之後的讀者要能重播)、JSON-RPC(MCP 的 AI 工具呼叫)——位置與理由見一張訂單的一生
反思一句話
通訊協定的選型不是「哪個最強」,是「哪個最 fit 你的場景 + 團隊熟悉度」。
REST 不是「最弱」,是「最通用」。gRPC 不是「最強」,是「在對的場景才強」。GraphQL 不是「現代化」,是「解決特定問題」。
選錯協定的成本很高——遷移要重寫大量 code、要重訓 team、要動到 client 端。所以第一次選的時候多想一下,比之後後悔便宜很多。
