RESTfulAPI 設計後端開發HTTP前端工程師前端教學

RESTful API 設計:前端工程師必學的 API 思維

HTTP Methods 怎麼選?Status Code 怎麼回?RESTful API 設計原則一次整理。從前端視角出發,幫你建立專業的 API 設計思維。

· 6 分鐘閱讀

上一篇文章學會了 Express,卻不知道自己的 API 設計是否合理?URL 要怎麼命名?什麼時候用 GET、什麼時候用 POST?如何決定 HTTP Status Code?這篇補足多數後端教學忽略的「API 設計」這塊,讓你從「會寫後端」升級到「會設計 API」。


前言:API 設計為什麼重要?

前端工程師最容易踩的坑是:學會了 Express 路由,卻把所有功能都用 POST 解決——反正 request.body 什麼都能傳,何必糾結要用 GET 還是 PUT?

這個心態會造成三個問題:

  • 前端開發困難:不同的人呼叫同一個 API,預期結果不一致,不知道該用哪個 method
  • 除錯地獄:當有 bug 時,光從 request log 無法判斷「這是查詢還是修改」,很難重現問題
  • 擴展性差:好的 API 設計是系統的骨架,爛的 API 會讓系統在需求變化時難以擴展

RESTful 不是銀彈,但它是一套經過大量實踐檢驗的共識。遵循 RESTful convention 的 API,不需要額外文件解釋「這個端點是做什麼的」,名稱本身就自帶語意。


HTTP Methods:不是只有 GET 和 POST

很多人誤以為 HTTP 只有 GET(拿資料)和 POST(送資料)兩種方法。實際上有五種最常用的:

Method語意是否 idempotent是否有 request body
GET取得資源✅ 是❌ 沒有
POST建立資源❌ 否✅ 有
PUT完整更新資源(替換)✅ 是✅ 有
PATCH部分更新資源❌ 否✅ 有
DELETE刪除資源✅ 是❌ 沒有

Idempotent 的意思是:無論你發一次還是發一百次,結果都一樣。GET 和 DELETE 天然 idempotent,PUT 也是,但 POST 和 PATCH 不是(每次都會建立新資源或產生副作用)。

什麼時候用 PUT vs PATCH?

這是最容易混淆的地方。記住這個原則:

  • PUT:替換整筆資源。客戶端必須傳完整的資源內容,缺少任何欄位會被當成「該欄位設為空」
  • PATCH:只更新指定欄位。客戶端只傳要改的部分,其餘不動
// PUT /api/users/1 — 完整更新
// 客戶端必須傳完整的使用者資料
{
  "name": "小明",
  "email": "xiaoming@example.com",
  "role": "admin"  // 如果漏掉這個欄位,server 會把 role 清除
}

// PATCH /api/users/1 — 部分更新
// 客戶端只傳要改的欄位
{
  "email": "new-email@example.com"  // 只有 email 會被更新,其餘不動
}

實務建議:大多數部分更新的場景用 PATCH 即可。除非你的系統有明確的「版本控制」需求,必須保留完整歷史,否則 PUT 的使用頻率相對低。


HTTP Status Code:不要只會回 200

後端新手最常見的錯誤,是無論成功失敗都回 200 OK,區別只在 response body 裡的 success 欄位。這樣前端必須每次都 parse body 才能知道結果——HTTP Status Code 的價值就是讓雙方不用看 body 就能知道發生了什麼事

最常用的幾個 Status Code

成功(2xx):

  • 200 OK:請求成功,response 有內容(GET、PATCH)
  • 201 Created:資源建立成功,通常在 POST 之後回傳,Header 會帶 Location 指向新資源
  • 204 No Content:成功但沒有內容要回傳,常用在 DELETE 之後

客戶端錯誤(4xx):

  • 400 Bad Request:請求格式有問題(少了必填欄位、格式錯誤、id 不存在)
  • 401 Unauthorized:未認證(沒有登入)
  • 403 Forbidden:已認證但沒有權限
  • 404 Not Found:資源不存在
  • 409 Conflict:資源衝突(例如建立時發現名稱重複)
  • 422 Unprocessable Entity:語法正確但語意錯誤(例如 email 格式對但不存在於允許清單)

伺服器錯誤(5xx):

  • 500 Internal Server Error:伺服器端的錯誤,通常是未捕捉的 exception
// Express 中的正確 Status Code 使用
app.post('/api/users', async (req, res) => {
  try {
    const { name, email } = req.body;

    // 簡單的驗證
    if (!name || !email) {
      return res.status(400).json({ error: 'name 和 email 是必填欄位' });
    }

    // 檢查 email 格式(簡單示範)
    if (!email.includes('@')) {
      return res.status(400).json({ error: 'email 格式不正確' });
    }

    // 建立資源
    const newUser = await db.users.create({ name, email });

    // 201 + Location header
    res.status(201)
      .set('Location', `/api/users/${newUser.id}`)
      .json(newUser);

  } catch (err) {
    // 500:不應該發生的錯誤
    console.error('Unexpected error:', err);
    res.status(500).json({ error: '伺服器錯誤,請稍後再試' });
  }
});

app.delete('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);

  if (!user) {
    return res.status(404).json({ error: '使用者不存在' });
  }

  await db.users.delete(req.params.id);
  // DELETE 成功不留 body
  res.status(204).end();
});

資源命名:URL 就是你的 API 文件

RESTful 的核心思想是:URL 代表「資源」,不是「動作」

壞的設計:

POST /api/createUser         ← 「建立使用者」是動作,不是資源
POST /api/updateUser?id=1   ← updateUser 是動作
GET  /api/getUserById?id=1  ← getUserById 是動作

好的設計:

POST   /api/users            ← 建立資源
GET    /api/users/1         ← 取得 id=1 的使用者
PUT    /api/users/1         ← 替換 id=1 的使用者
PATCH  /api/users/1         ← 部分更新 id=1 的使用者
DELETE /api/users/1          ← 刪除 id=1 的使用者

URL 本身就是文件。看到 /api/users/1 就知道是操作 id=1 的使用者資源,不需要另外說明。

巢狀資源怎麼處理?

如果資源有從屬關係,URL 也可以巢狀:

GET /api/users/1/orders        ← 取得 user 1 的所有訂單
GET /api/users/1/orders/5      ← 取得 user 1 的訂單 5
POST /api/users/1/orders        ← 為 user 1 建立訂單

但要注意:巢狀層級不要超過兩層,否則 URL 會變得太長太耦合。如果出現三層以上的巢狀,考慮用 query parameter 或把資源拉平:

# 太深的巢狀
GET /api/orgs/1/teams/2/members/3

# 更好的設計:直接用成員 ID
GET /api/members/3

前端視角:API 設計不良的痛苦

身為前端工程師,你一定遇過這種 API:

// 這個 API 是要做什麼?
// 查詢?建立?更新?看不出來
POST /api/handleUser

// 回傳這個到底是成功還是失敗?
{
  "code": 0,
  "data": { ... },
  "message": "ok"
}

好的 RESTful API 讓前端呼叫時語意清晰:

// 查詢使用者——語意清楚
const user = await fetch('/api/users/1').then(r => r.json());

// 建立訂單——回 201,前端就知道是新建成功
const order = await fetch('/api/users/1/orders', {
  method: 'POST',
  body: JSON.stringify({ amount: 1000 })
}).then(r => r.status === 201 ? r.json() : null);

前端視角的 API 設計原則:

  1. 可預測:同一個 resource 的操作,用同樣的 URL pattern
  2. 語意明確:從 URL 和 method 就能知道要幹嘛,不需要文件
  3. Status Code 有意義:不要讓前端必須 parse body 才能判斷成敗
  4. 錯誤訊息具體400: "email 格式不正確" 優於 400: "操作失敗"

常見問題

Q:RESTful 是強制的嗎?

A:不是。RESTful 是一種風格,不是規範。很多大型系統(GitHub、Stripe)有自己的 API 設計慣例,不完全符合 RESTful 但仍然非常好用。重點是團隊內部保持一致,有自己的 conventions 寫在文件裡,比強迫遵守 RESTful 更有價值。

Q:如果一個操作涉及多個資源怎麼辦?

A:例如「把使用者移到另一個部門」需要同時更新 User 和 Department。幾種做法:

  • 分散式請求:前端分兩次呼叫(先 PATCH User,再更新 Department)
  • Batch API:設計 POST /api/batch,一次送多個操作
  • Transaction:後端包成一個 transaction,前端一次呼叫

實務上分散式請求最常見,但缺點是中途失敗會造成狀態不一致。重要操作建議用 Batch 或 Transaction。

Q:需要版本控制(v1、v2)嗎?

A:小型專案不需要。版本控制是當 API 要breaking change 但又必須向後相容時才需要的機制。一開始設計好的 API 通常不需要到 v2,過早版本控制只會增加維護負擔。


總結:API 設計是給未來的自己用的

這篇文章的核心只有一個概念:好的 API 設計,是讓別人(或者未來的自己)看見 URL 就能預期會發生什麼

HTTP Method、Status Code、URL 命名——這些不是學術規定,是減少溝通成本的實務工具

三個快速檢查清單,下次設計 API 前問自己:

  1. 這個操作用對 HTTP Method 了嗎?——能用 GET 就不要用 POST
  2. Status Code 有意義嗎?——成功失敗看 status code 就知道,不需要 parse body
  3. URL 是資源導向嗎?——URL 代表「東西」,不是「動作」

遵循這些原則,你的 API 會比大多數線上教程的範例還要專業。


本篇是「前端工程師後端入門」系列第二篇。第一篇《從 Node.js 原生 HTTP 到 Express 框架》說明了框架的價值,本篇則聚焦在如何設計 RESTful API。