【系統設計 30 概念】#02 API 設計與資料儲存 — REST、GraphQL 與資料庫選型

測驗:API 設計與資料儲存 — REST、GraphQL 與資料庫選型

共 5 題,點選答案後會立即顯示結果

1. 為什麼 Client 不直接存取資料庫,而是透過 API 溝通?

  • A. 因為資料庫不支援 HTTP 協定,所以必須透過 API 轉換
  • B. 因為 API 可以加速資料庫查詢,讓回應更快
  • C. 因為 API 提供安全性驗證、前後端解耦、以及流量控制等保障
  • D. 因為資料庫只能存放文字,需要 API 來處理其他格式

2. 在 REST API 中,如果要「建立一個新使用者」,應該使用哪個 HTTP 方法搭配哪個 URL?

  • A. GET /users
  • B. POST /users
  • C. PUT /users/new
  • D. DELETE /users

3. REST API 的「Stateless(無狀態)」特性意味著什麼?

  • A. Server 不會儲存任何資料到資料庫
  • B. Client 不需要提供身份驗證資訊
  • C. API 不能回傳 JSON 格式的資料
  • D. 每個請求獨立,Server 不記住上一個請求的內容,Client 每次都要帶上身份資訊

4. GraphQL 主要解決了 REST 的哪兩個問題?

  • A. 安全性不足和回應速度太慢
  • B. Over-fetching(拿到不需要的欄位)和 Under-fetching(一次拿不到所有需要的資料)
  • C. 不支援 JSON 格式和無法處理巢狀資料
  • D. 無法水平擴展和不支援即時通訊

5. 你正在開發一個電商系統,需要處理訂單和庫存,而且「扣款和出貨」必須綁在一起(全成功或全失敗)。根據文章的決策框架,你應該選擇哪種資料庫?為什麼?

  • A. SQL 資料庫(如 PostgreSQL),因為需要 ACID 交易保證和複雜的跨表 JOIN 查詢
  • B. NoSQL 資料庫(如 MongoDB),因為電商資料格式多變,需要彈性 Schema
  • C. Redis,因為電商需要毫秒級的讀寫速度
  • D. NoSQL 資料庫,因為電商系統需要水平擴展來應對大量使用者

你知道 client 和 server 會互相溝通,但「API」到底是什麼角色?REST 和 GraphQL 差在哪?資料庫為什麼有 SQL 和 NoSQL 兩大陣營?這篇文章幫你建立判斷框架。

這篇文章解決什麼問題?

如果你有以下困惑,這篇文章適合你:

  • Client 為什麼不直接存取資料庫,中間那層 API 到底在幹嘛?
  • REST 和 GraphQL 都是 API,到底什麼時候該用哪個?
  • 資料庫選型時,SQL 和 NoSQL 怎麼選?

前置知識

這篇假設你已經讀過第 1 篇,理解 Client-Server 架構和 HTTP 基本概念。


1. API — Client 和 Server 之間的「中間人」

一句話說明

API(Application Programming Interface)是 client 和 server 之間的溝通合約,定義了「你可以問什麼、怎麼問、會得到什麼回答」。

為什麼不讓 client 直接存取資料庫?

想像一個場景:你在手機 App 上查自己的訂單。

直接存取資料庫(危險!不會這樣做):
App → 直接連 Database → SELECT * FROM orders WHERE user_id = 123

透過 API(正確做法):
App → 發 HTTP 請求給 API → API 驗證身份 → API 查資料庫 → 回傳結果

為什麼要多這一層?三個關鍵原因:

原因 解釋
安全性 API 可以驗證身份、過濾請求。直接連資料庫等於把鑰匙交出去
解耦 前端和後端可以獨立開發。只要 API 合約不變,雙方隨便改
控制 API 可以限制流量、記錄操作、轉換資料格式

最小範例:API 的 Request-Response 模式

Client 發出請求(Request):
GET /api/orders/456

Server 回傳回應(Response):
{
  "id": 456,
  "status": "shipped",
  "total": 1200
}
Code language: JavaScript (javascript)

翻譯:Client 問「456 號訂單的狀況如何?」,Server 回答「已出貨,總共 1200 元」。

每一次 API 溝通都遵循這個模式:Request(問)→ Response(答)


2. REST API — 目前最主流的 API 風格

一句話說明

REST 用 URL 代表「資源」,用 HTTP 方法表示「要對資源做什麼」。

核心概念翻譯

你會看到 意思
GET /users/123 讀取 ID 為 123 的使用者
POST /users 建立一個新使用者
PUT /users/123 更新 ID 為 123 的使用者(整個替換)
DELETE /users/123 刪除 ID 為 123 的使用者
Stateless 每個請求獨立,server 不記得上一個請求

逐行翻譯:一個典型的 REST API 呼叫

# 建立一個新使用者
curl -X POST https://api.example.com/users \   # 對 /users 發出 POST 請求(建立)
     -H "Content-Type: application/json" \      # 告訴 server:我送的是 JSON 格式
     -H "Authorization: Bearer eyJhb..." \      # 附上身份驗證 token
     -d '{"name": "Alice", "email": "[email protected]"}' # 要建立的資料
Code language: PHP (php)

Server 回應:

{
  "id": 789,
  "name": "Alice",
  "email": "[email protected]",
  "created_at": "2026-02-23T10:00:00Z"
}
Code language: JSON / JSON with Comments (json)

翻譯:「我要在 /users 這個集合裡建立一筆新資料,資料內容是 name 和 email,server 回傳建立好的完整資料(包含自動產生的 id 和時間)」。

REST 的設計哲學:Resource-based

REST 的核心是「資源」。每個 URL 代表一個資源,HTTP 方法代表動作:

資源(名詞)  +  動作(動詞)  =  API 端點

/users         GET            → 取得所有使用者
/users/123     GET            → 取得特定使用者
/users         POST           → 建立新使用者
/users/123     PUT            → 更新特定使用者
/users/123     DELETE         → 刪除特定使用者

Stateless — 無狀態是什麼意思?

有狀態(Stateful)的對話:
你:「我要查訂單」
店員:(記住你是誰)「好的,王先生,你的訂單...」

無狀態(Stateless)的對話:
你:「我是王先生,這是我的會員卡,我要查訂單 456」
店員:「好的,456 號訂單已出貨」
你:「我是王先生,這是我的會員卡,我要查訂單 789」
店員:「好的,789 號訂單處理中」

每次請求都要「重新自我介紹」(帶上 token),server 不會記住你上次問了什麼。這看起來多餘,但好處是:任何一台 server 都能處理你的請求,不需要找到「上次跟你聊過的那台」,這對大規模系統很重要。


3. GraphQL — 讓 Client 決定要什麼資料

一句話說明

GraphQL 讓 client 精確描述「我要哪些欄位」,一次請求拿到所有需要的資料。

REST 的痛點:Over-fetching 與 Under-fetching

想像你在做一個「使用者個人頁面」,需要顯示使用者名稱和他的訂單列表。

用 REST,你可能需要兩個請求:

1. GET /users/123
   回傳:{ id, name, email, phone, address, birthday, ... }
   → Over-fetching:你只需要 name,卻拿到一堆不需要的欄位

2. GET /users/123/orders
   回傳:[{ id, status, total, items, ... }, ...]
   → 要發第二個請求才能拿到訂單
   → Under-fetching:一個請求拿不到你要的所有資料

GraphQL 怎麼解決

# 用 GraphQL,一個請求搞定,而且只拿你要的欄位:

query {
  user(id: 123) {    # 我要 ID 123 的使用者
    name             # 只要名字
    orders {         # 還有他的訂單
      id             # 訂單只要 id
      status         # 和狀態
    }
  }
}
Code language: PHP (php)

Server 回應:

{
  "data": {
    "user": {
      "name": "Alice",
      "orders": [
        { "id": 456, "status": "shipped" },
        { "id": 789, "status": "pending" }
      ]
    }
  }
}
Code language: JSON / JSON with Comments (json)

翻譯:「給我 123 號使用者的名字,還有他所有訂單的 id 和狀態,其他的我不要」。

REST vs GraphQL 快速決策樹

Q1: 你的前端需要的資料格式是否經常變動?
├─ 經常變(快速迭代中)→ 考慮 GraphQL
└─ 很穩定 → 繼續 Q2

Q2: 前端需要的資料是否需要多次 API 呼叫才能湊齊?
├─ 是(要打好幾個 endpoint)→ 考慮 GraphQL
└─ 一個 endpoint 就能搞定 → 繼續 Q3

Q3: 你的團隊有 GraphQL 經驗嗎?
├─ 有 → 用 GraphQL
└─ 沒有 → 用 REST(學習成本低,生態成熟)

情境速查表

情境 建議 原因
簡單 CRUD 應用 REST 直覺、工具成熟、夠用
手機 App(省流量) GraphQL 只拿需要的欄位,省頻寬
多種 client 共用同一 API GraphQL 各 client 自訂查詢
第三方開放 API REST 易理解、文件好寫
微服務之間的溝通 REST(或 gRPC) 簡單直接,效能好
複雜的巢狀資料查詢 GraphQL 一次查詢取得所有關聯資料

常見坑:GraphQL 不是萬靈丹

症狀:聽說 GraphQL 比 REST 好,全面改用 GraphQL,結果開發變慢。

為什麼會發生: GraphQL 需要額外的學習成本(Schema 定義、Resolver 撰寫、Client 端 cache 管理)。對於簡單的 CRUD 應用,REST 的 convention 已經夠用,引入 GraphQL 反而增加複雜度。

判斷原則: 如果你的 API 用 REST 寫起來很順,沒有遇到 over-fetching 或 under-fetching 的痛點,就不需要切換到 GraphQL。解決真實存在的問題,不要解決假想的問題。


4. Database — 為什麼不直接用檔案存資料?

一句話說明

資料庫是專門用來高效儲存、查詢、管理大量資料的系統。

檔案 vs 資料庫

用檔案存資料:
- 想找一筆訂單? → 讀取整個檔案,一行行找
- 兩個人同時改資料? → 檔案可能被覆蓋
- 想保證「扣款和出貨」一起完成? → 自己寫邏輯,超容易出 bug

用資料庫:
- 想找一筆訂單? → 用索引(Index),毫秒級找到
- 兩個人同時改資料? → 資料庫自動處理並發(Concurrency Control)
- 想保證「扣款和出貨」一起完成? → 交易(Transaction),全成功或全失敗

資料庫提供的三大保障

保障 解釋 類比
索引(Index) 幫資料建目錄,加速查詢 書本的索引頁,不用翻完整本書
交易(Transaction) 多個操作綁在一起,全成功或全失敗 銀行轉帳:扣款和入帳必須同時完成
並發控制(Concurrency) 多人同時操作不會互相干擾 多人同時編輯 Google Docs 不會覆蓋彼此

5. SQL vs NoSQL — 資料庫怎麼選?

一句話說明

SQL 是「結構明確的表格」,NoSQL 是「彈性的資料容器」。

最小範例對照

SQL(以 PostgreSQL 為例)

-- 資料長什麼樣(Schema),必須事先定義
CREATE TABLE users (
    id    INT PRIMARY KEY,    -- 每個使用者有唯一 ID
    name  VARCHAR(100),       -- 名字,最多 100 字
    email VARCHAR(200)        -- 信箱,最多 200 字
);

-- 查詢:找出所有訂單金額超過 1000 的使用者
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id   -- 把兩張表連起來
WHERE orders.total > 1000;                 -- 條件:金額超過 1000

翻譯:先定義「使用者長什麼樣」(像 Excel 的欄位標題),然後用 SQL 語言跨表格查詢。

NoSQL(以 MongoDB 為例)

// 直接塞資料,不用事先定義 Schema
db.users.insertOne({
    name: "Alice",
    email: "[email protected]",
    orders: [                      // 訂單直接塞在使用者裡面
        { id: 1, total: 1500 },
        { id: 2, total: 800 }
    ]
})

// 查詢:找出有訂單金額超過 1000 的使用者
db.users.find({ "orders.total": { $gt: 1000 } })
Code language: PHP (php)

翻譯:資料像 JSON 一樣塞進去,不用事先定義格式。相關的資料(訂單)直接放在同一個文件裡。

核心差異比較

面向 SQL(關聯式) NoSQL(非關聯式)
資料結構 固定欄位(表格) 彈性結構(JSON-like)
關聯性 用 JOIN 連接多張表 通常把相關資料放在同一份文件
Schema 必須事先定義 隨時可以新增欄位
一致性保證 ACID(強一致性) 多數是最終一致性
擴展方式 垂直擴展(升級機器) 水平擴展(加更多機器)
查詢語言 標準 SQL 各家不同(MongoDB 用 JSON 語法)

ACID 是什麼?一張圖看懂

ACID 就是資料庫的四個保證:

A - Atomicity(原子性)
    「全做或全不做」
    例:轉帳時,扣款和入帳要麼都成功,要麼都不做

C - Consistency(一致性)
    「資料永遠合理」
    例:帳戶餘額不會變成負數

I - Isolation(隔離性)
    「互不干擾」
    例:兩個人同時轉帳,結果和一個一個轉一樣

D - Durability(持久性)
    「存了就不會丟」
    例:系統當機重啟後,資料還在

SQL vs NoSQL 快速決策樹

Q1: 你的資料之間有明確的關聯嗎?(例如:使用者有訂單,訂單有商品)
├─ 有,而且經常需要跨表查詢 → SQL
└─ 沒有太多關聯,或可以反正規化 → 繼續 Q2

Q2: 資料格式會經常改變嗎?
├─ 會(快速迭代、每個物件欄位不同)→ NoSQL
└─ 不太會 → 繼續 Q3

Q3: 你需要強一致性(ACID)嗎?
├─ 需要(金融、庫存等不能出錯的)→ SQL
└─ 可以接受短暫的不一致(社群貼文、日誌等)→ NoSQL

Q4: 你預期要處理多大量的資料?
├─ 超大量 + 需要水平擴展 → NoSQL
└─ 中等規模 → SQL(PostgreSQL 已經很能打了)

情境速查表

情境 建議 原因
電商系統(訂單、庫存) SQL(PostgreSQL) 需要 ACID、複雜 JOIN 查詢
社群平台的動態牆 NoSQL(MongoDB) 資料格式多變、讀取量大
使用者認證系統 SQL 帳號資料需要強一致性
IoT 感測器資料 NoSQL / 時序資料庫 大量寫入、結構簡單
快取層 Redis(NoSQL) 毫秒級讀寫、key-value 結構
新創 MVP 快速開發 SQL(PostgreSQL) 萬用、社群大、遇到問題好找解答

常見坑:NoSQL 不等於不需要設計

症狀:選了 MongoDB,以為不用管 Schema,結果資料越來越亂,查詢越來越慢。

為什麼會發生: NoSQL 的「彈性 Schema」不代表「不需要設計」。你還是需要根據查詢模式來設計資料結構。例如,MongoDB 的效能高度依賴你怎麼組織文件(Document)— 哪些資料要嵌入(embed),哪些要引用(reference)。

怎麼避免: 先想清楚「你的資料會怎麼被查詢」,再決定資料怎麼存。跟 AI 說:

「我的應用有使用者和訂單兩種資料,主要的查詢場景是 [列出場景]。我用 MongoDB,幫我設計 Document 結構,並解釋為什麼這樣設計。」


完整對照:一個請求從 Client 到 Database

把這篇講的所有概念串起來:

1. 使用者在手機 App 按下「查看訂單」

2. App(Client)發出 REST API 請求:
   GET /api/orders?user_id=123
   Authorization: Bearer eyJhb...

3. Server 收到請求,API 層處理:
   - 驗證 token(這人是誰?有權限嗎?)
   - 解析參數(要查 user_id=123 的訂單)

4. Server 查詢資料庫:
   SELECT * FROM orders WHERE user_id = 123;

5. 資料庫回傳結果,Server 組裝回應:
   { "orders": [{ "id": 456, "status": "shipped" }] }

6. App 收到回應,顯示在畫面上
Code language: JavaScript (javascript)

小結

記住這個判斷框架

  1. API 是必要的中間層 — 安全性、解耦、控制,這三個理由讓 API 成為標準做法
  2. REST vs GraphQL — 大部分情境 REST 就夠了。遇到 over-fetching / under-fetching 的痛點再考慮 GraphQL
  3. SQL vs NoSQL — 資料關聯明確 + 需要強一致性 = SQL;資料格式多變 + 需要水平擴展 = NoSQL

最重要的原則: 選技術要看你的實際需求,不要因為某個技術「比較新」或「大公司在用」就選它。大部分情況下,PostgreSQL + REST API 就能解決 90% 的問題。

當你不確定時,跟 AI 說

「我在做一個 [應用類型],資料有 [描述資料關聯],預期規模是 [使用者數 / 資料量]。REST 和 GraphQL 哪個適合?資料庫該選 SQL 還是 NoSQL?幫我分析優缺點。」


下一篇預告

第 3 篇我們會進入「API 架構模式」,討論 Proxy、Load Balancer,以及 API Gateway — 當你的系統不只一台 server 時,要怎麼管理流量和路由。

進階測驗:API 設計與資料儲存

測驗目標:驗證你是否能在實際情境中應用 REST、GraphQL 與資料庫選型的知識。
共 5 題,包含情境題與錯誤診斷題。

1. 手機 App 的 API 選型 情境題

你正在開發一個手機 App,首頁需要同時顯示: – 使用者的名稱和頭像 – 最近 5 筆訂單的狀態 – 3 則未讀通知的標題 目前後端使用 REST API,你需要分別呼叫: GET /users/123 GET /users/123/orders?limit=5 GET /users/123/notifications?unread=true&limit=3 前端團隊反映首頁載入太慢,而且每個 API 都回傳了很多用不到的欄位。

作為技術決策者,你會建議哪個方案?

  • A. 把三個 REST endpoint 合併成一個 GET /homepage,回傳所有需要的資料
  • B. 導入 GraphQL,讓前端用一個 query 精確指定需要的欄位和資料
  • C. 使用 WebSocket 讓 server 主動推送首頁資料給 client
  • D. 在前端加上快取,把三個 API 的結果存在本地,減少請求次數

2. 新創 MVP 的資料庫選型 情境題

你的團隊正在開發一個電商 MVP,核心功能包括: – 使用者帳號管理(註冊、登入) – 商品目錄(有固定欄位:名稱、價格、庫存) – 訂單系統(下單時要同時扣庫存、建立訂單、扣款) 團隊成員建議用 MongoDB,理由是「NoSQL 比較快、比較新、 大公司都在用」。

你會怎麼回應這個建議?

  • A. 同意使用 MongoDB,因為 NoSQL 確實比 SQL 快,而且 Schema 比較彈性,適合快速開發
  • B. 建議同時使用 PostgreSQL 和 MongoDB:PostgreSQL 存訂單,MongoDB 存商品目錄
  • C. 建議使用 PostgreSQL,因為電商需要 ACID 交易保證,且資料之間有明確關聯
  • D. 建議使用 MongoDB 但搭配 Redis 做快取,來彌補 NoSQL 在一致性上的不足

3. API 中間層的必要性 情境題

你的同事正在做一個內部工具,他想讓前端直接連資料庫 來簡化架構: 同事的說法: 「這只是內部工具,不需要對外開放, 沒有安全性疑慮。直接連 DB 還比較快, 不用多寫一層 API 浪費時間。」 目前這個工具有 3 個前端頁面, 分別需要查詢不同的資料表。

你會怎麼說服同事加上 API 層?

  • A. 安全性是唯一原因,既然是內部工具就不需要 API 層,同事的做法沒問題
  • B. 直接連 DB 無法使用 SQL 語法,所以一定需要 API 層來轉譯查詢
  • C. 加 API 層的主要原因是效能,API 可以做快取讓查詢更快
  • D. 除了安全性,API 層還提供解耦和控制的好處 — 前後端可獨立修改,也能限流和記錄操作

4. REST API 設計錯誤 錯誤診斷

後端工程師設計了以下 REST API: POST /getUser — 取得使用者資料 POST /deleteUser/123 — 刪除使用者 GET /createOrder — 建立新訂單 PUT /users — 更新特定使用者

這組 API 設計的核心問題是什麼?

  • A. 路徑中不應該包含數字 ID,應該用 query parameter 傳遞
  • B. HTTP 方法和動作不匹配 — 讀取用了 POST、刪除用了 POST、建立用了 GET,違反 REST 的 resource-based 設計原則
  • C. 路徑命名不統一,應該全部改用複數形式如 /users
  • D. 缺少版本號,應該加上 /v1/ 前綴

5. MongoDB 效能問題診斷 錯誤診斷

團隊選用 MongoDB 開發社群平台,初期資料結構設計如下: // users collection { _id: “user_001”, name: “Alice” } // posts collection { _id: “post_001”, author_id: “user_001”, // 引用 user content: “Hello world”, comments: “comment_001” // 引用 comment } // comments collection { _id: “comment_001”, post_id: “post_001”, // 引用 post author_id: “user_001”, // 引用 user text: “Nice post!” } 顯示一篇貼文(含作者名稱和所有留言)需要查詢 3 次。 隨著資料量增加,頁面載入越來越慢。

這個設計的根本問題是什麼?

  • A. MongoDB 本身效能不好,應該換成 PostgreSQL
  • B. 缺少索引(Index),應該對 author_idpost_id 建立索引就能解決
  • C. 用了關聯式的設計思維來使用 NoSQL — 應該將留言嵌入(embed)貼文文件中,減少跨 collection 查詢
  • D. MongoDB 不支援 JOIN 查詢,所以應該在前端分別請求再組合資料

發佈留言

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