iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0

RESTful API 是一種將網路上所有事物視為「資源 (Resource)」的架構風格,也是目前業界前後端分離最主流的通訊標準。它不是一種程式語言,也不是硬性規定的底層語法,而是一套「優雅的設計默契」。遵守這套默契,你的 API 就能讓任何接手的前端工程師或第三方開發者一目了然。

昨天我們用 express.Router 拆分了路由,今天我們要為這些路由制定最嚴謹的命名與回傳標準。

1. 核心精神:名詞定義資源,動詞決定動作

在 RESTful 的世界裡,網址 (URL) 只能出現名詞,用來標示你要操作的目標(資源);而你要對它做什麼事,全權交給 HTTP Method (GET, POST, PUT, DELETE) 來決定。

假設你正在開發一款 RPG 遊戲,裡面有一個核心角色叫做「無名神 (Nameless God)」,你需要設計存取這個角色與其裝備的 API。

傳統的超雷命名法 (動詞混雜):

  • /getNamelessGod (取得角色)
  • /createCharacter (創建角色)
  • /updateWeapon (更新武器)
  • /deleteItem (刪除道具)
  • 缺點:網址無限膨脹,沒有統一標準,前端猜不到你的命名邏輯。

RESTful 降維打擊命名法:

  • GET /characters (取得所有角色清單)
  • POST /characters (創建一個新角色)
  • GET /characters/nameless-god (讀取特定角色「無名神」的資料)
  • PUT /characters/nameless-god (覆蓋/更新「無名神」的整體資料)
  • DELETE /characters/nameless-god (刪除「無名神」這個角色)

你看出來了嗎?網址完全沒有變過,永遠都是 /characters,我們只透過切換 HTTP Method,就完美表達了四種完全不同的操作意圖。

2. 路由命名四大金律

在設計 RESTful API 時,請將這四條規則刻在你的鍵盤上:

  1. 一律使用複數名詞:無論是取得單筆還是多筆資料,資源名稱都應該使用複數。例如:/users, /products, /orders。
  2. 全部小寫,單字用連字號 (-) 隔開:不要用駝峰式命名 (camelCase) 或底線 (_)。使用 /hero-equipments 而不是 /heroEquipments 或 /hero_equipments。
  3. 利用階層表達關聯性:如果資源之間有從屬關係,直接在網址上展現出來。
    • 例如,想取得無名神背包裡的「第一把劍 (My First Sword)」:
    • GET /characters/nameless-god/inventory/my-first-sword
  4. 不要在網址加上副檔名:不要寫 /characters.json。資料格式是由 HTTP Request Header 的 Accept 或 Content-Type 來決定的。

3. 善用 HTTP 狀態碼 (Status Codes)

一個專業的後端工程師,不會讓前端永遠只收到 200 OK,然後把錯誤訊息包裝在 JSON 裡面。你必須讓 HTTP 狀態碼在第一線就精準表達結果:

  • 2xx (成功群組)
    • 200 OK:最常見的成功回應(適用於 GET, PUT, DELETE)。
    • 201 Created:資源成功建立(專屬 POST 新增資料成功時使用)。
  • 4xx (前端/客戶端犯錯)
    • 400 Bad Request:前端傳來的資料格式錯誤、漏傳必填欄位。
    • 401 Unauthorized:未登入,或是沒有帶上有效的 Token。
    • 403 Forbidden:已登入,但你的權限不足(例如一般玩家想呼叫管理員專用的 API)。
    • 404 Not Found:請求的資源(如某個角色 ID 或網址)不存在。
  • 5xx (後端/伺服器犯錯)
    • 500 Internal Server Error:伺服器內部錯誤(你的程式碼寫爛了,或是資料庫掛了)。

4. 實戰演練:重構你的 API 回應

讓我們把 Day 9 的路由加上精準的狀態碼。打開你昨天的 routes/users.js,修改其中的 POST 邏輯:

// POST /api/users - 新增使用者
router.post('/', (req, res) => {
    const { username, email } = req.body;

    // 1. 驗證資料:如果漏傳欄位,回傳 400
    if (!username || !email) {
        // return 會提早結束函式,防止繼續往下執行
        return res.status(400).json({ 
            error: 'Bad Request', 
            message: 'username 與 email 為必填欄位' 
        });
    }

    // 2. 模擬寫入資料庫成功,回傳 201 Created
    const newUser = { id: 101, username, email };
    
    res.status(201).json({ 
        message: '角色創建成功',
        data: newUser 
    });
});

上一篇
Day 9:路由(Routing)設計美學與模組化拆分
下一篇
Day 11: Express 的靈魂核心—— Middleware 流程與機制實戰
系列文
不要再說你不會後端!30 天 Node.js 降維打擊指南 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言