API 的進化之路:從 XML 到 GraphQL,打造跨系統的共同語言

更新日期: 2025 年 3 月 4 日

當程式也需要「共同語言」

想像一個跨國會議:來自不同國家的人們,必須用同一種語言溝通才能達成共識。

在軟體世界中,API(應用程式介面)就是這樣的「共同語言」,它讓前端、後端、手機 App、甚至 IoT 設備能互相理解。

本文將揭開 API 設計的基礎面紗,從資料格式的演變(XML → JSON)到三大主流風格(RPC、REST、GraphQL),帶你掌握選擇溝通方式的關鍵思維。


資料格式演變:從 XML 到 JSON 的進化史

XML:結構嚴謹的「正式公文」

在 1990 年代末至 2000 年代初,XML 是資料交換的主要格式。

它的誕生是為了解決不同系統間的數據傳輸問題,並廣泛應用於企業級應用、政府系統與標準化文件處理。

XML 的特性

  • 使用 標籤(Tag) 來定義資料結構,類似於 HTML,但更具描述性與自定義能力。
  • 強調結構的嚴謹性,通常需要搭配 XSD(XML Schema Definition)DTD(Document Type Definition) 來定義格式,確保資料的一致性與正確性。
  • 支援巢狀結構,適合處理層級較深、關係複雜的數據。
  • 跨平台支援良好,被許多企業級應用與 Web 服務(如 SOAP)採用。

XML 的範例

以下是一個透過 XML 傳遞商品資訊的例子:

<product>
  <id>123</id>
  <name>無線鍵盤</name>
  <price>1990</price>
  <stock>true</stock>
</product>

XML 的優缺點

優點:

  • 適合 複雜資料結構,能夠清楚地描述資料層級與關聯性。
  • 提供 良好的擴展性,可以透過命名空間(Namespace)避免標籤名稱衝突。
  • 適用於 企業級系統,特別是金融、政府、醫療等領域。

缺點:

  • 資料冗長:標籤占據較大比例,導致文件大小較大。
  • 解析速度較慢:需要透過 DOM 或 SAX 解析器來處理,耗費較多效能。
  • 對 JavaScript 不友好:瀏覽器端處理 XML 需要額外的解析步驟,相較於 JSON 來說不夠直覺。

JSON:輕量化的「便利貼」

隨著 Web 2.0、AJAX(Asynchronous JavaScript and XML)技術的普及,以及 JavaScript 在前端開發中的主導地位,JSON 漸漸取代 XML 成為 Web API 的主要資料格式。

JSON 的特性

  • 語法源自 JavaScript 物件,可以直接被 JavaScript 解析與使用。
  • 結構 簡潔明瞭,不需要額外的標籤來描述數據,僅使用大括號 {} 和方括號 [] 來組織資料。
  • 解析速度快,能夠直接轉換為 JavaScript 物件,無需額外的解析步驟。
  • 天然適合 Web 開發,廣泛應用於 RESTful API、前端與後端數據交換、行動應用 等領域。

JSON 的範例

與 XML 相同的商品資訊,若使用 JSON 表達則如下所示:

{
  "product": {
    "id": 123,
    "name": "無線鍵盤",
    "price": 1990,
    "inStock": true
  }
}

JSON 的優缺點

優點:

  • 輕量簡潔,去除了多餘的標籤,資料體積小,適合網路傳輸。
  • 解析速度快,可直接轉換為 JavaScript 物件,減少處理時間。
  • 對 JavaScript 友好,不需要額外的解析工具即可使用,適合現代 Web 開發。

缺點:

  • 缺乏嚴格的格式驗證,不像 XML 具有 XSD 或 DTD,容易導致格式錯誤。
  • 不適合高度結構化的數據,例如需要描述複雜關聯的企業級系統。
  • 安全性問題,若未妥善處理 JSON,可能會受到 XSS(跨站腳本攻擊)影響。

資料格式對比表

特性XMLJSON
資料類型需額外定義(XSD)原生支援字串、數字、布林值
檔案大小較大(標籤重複)較小(無冗餘標籤)
解析速度慢(需透過 DOM 解析)快(可直接轉換為 JS 物件)
可讀性可讀性較低,標籤冗長可讀性較高,結構簡潔
適用場景企業系統、標準化文件(如銀行、醫療)Web API、行動應用、前後端數據交換

XML 與 JSON 的轉變:關鍵影響因素

XML 曾經是 Web 開發中的主要資料格式,但隨著技術環境的變遷,JSON 逐漸成為主流,這背後有幾個重要的關鍵影響因素:

  1. 行動網路時代的來臨
    • XML 的冗長結構使其在行動環境下不夠高效,而 JSON 的輕量特性更適合節省頻寬的需求。
  2. AJAX 與 Web 2.0 的崛起
    • AJAX 允許前端與伺服器進行非同步數據交換,而 JSON 由於與 JavaScript 的高兼容性,成為 AJAX 開發的最佳選擇。
  3. REST API 的流行
    • 相較於傳統的 SOAP(Simple Object Access Protocol)API,RESTful API 更簡單、易讀,而 JSON 作為 REST API 的主要數據格式,推動了它的普及。
  4. 瀏覽器與後端技術的支援
    • 現代瀏覽器與後端框架(如 Node.js、Python Flask、Ruby on Rails)皆內建 JSON 解析功能,使其成為 Web 開發的標準格式。

延伸閱讀:JSON 的故事:Douglas Crockford 的訪談


API 風格大亂鬥:RPC、REST、GraphQL 的設計哲學

在現代軟體開發中,API(Application Programming Interface)扮演著關鍵角色,它是不同系統、服務之間溝通的橋樑。

隨著需求的多樣化,開發者設計出了 RPC(Remote Procedure Call)REST(Representational State Transfer)GraphQL 三種主流 API 風格。

這三種 API 各有其核心理念與適用場景,選擇合適的設計模式能夠提升系統效能、可維護性與開發效率。

以下將深入剖析它們的設計哲學、優缺點與應用場景。

RPC(遠程過程調用):像打電話下指令

核心理念

RPC 的設計目標,是讓呼叫遠端 API 像執行本地函數一樣簡單,不需要關心底層通訊細節,只需提供方法名稱與參數即可執行動作。

這種風格適合內部系統,特別是需要頻繁調用遠端方法的場景,例如微服務架構中的服務間通訊。

RPC 的特性

  • 方法導向:API 的主要概念是「動作」,而非「資源」。
  • 通常透過 HTTP POST 發送請求,但也可以使用其他協議(如 gRPC 基於 HTTP/2)。
  • 方法名稱與參數封裝在請求體內,而不是像 REST API 那樣依賴 URL 路徑。

RPC API 呼叫範例

POST /api
Content-Type: application/json

{
  "method": "getProduct",
  "params": { "id": 123 }
}

優缺點

優點:

  • 直覺易用:開發者只需關心「調用什麼方法」,不必設計 API 資源模型。
  • 適合內部微服務:可以讓服務之間進行高效能的函數級調用,如 gRPC(Google RPC)。

缺點:

  • 缺乏統一標準:不同系統可能有不同的 API 格式,導致維護成本增加。
  • 擴展性較差:當 API 方法數量增加時,命名與管理變得困難,例如 getProductV1getProductV2 可能會讓 API 混亂。
  • 難以符合 RESTful 風格的標準化設計,如 HTTP 狀態碼、快取等特性。

REST:以資源為中心的「寫信溝通」

核心理念

REST 的設計哲學是 將所有事物視為「資源」(Resource),並透過 HTTP 方法來進行操作,符合 Web 既有的標準。

這種風格使 API 易於理解、維護,並具有高度的可擴展性。

REST 的特性

  • 以「資源」為核心,而非函數調用。
  • 使用標準 HTTP 方法:
    • GET:取得資源
    • POST:新增資源
    • PUT:更新資源
    • DELETE:刪除資源
  • 無狀態(Stateless):每次請求都是獨立的,不依賴伺服器端的會話狀態。
  • 統一介面(Uniform Interface):API 使用標準化的 HTTP 狀態碼(如 200、404、500)來回應客戶端請求。

RESTful API 呼叫範例

GET    /products/123      # 取得商品 123
POST   /products          # 新增商品
PUT    /products/123      # 更新商品 123
DELETE /products/123      # 刪除商品 123

優缺點

優點:

  • 標準化:符合 HTTP 規範,易於理解與使用。
  • 可快取:GET 請求可利用瀏覽器或 CDN 快取,提高效能。
  • 適合公開 API:如社群網站、電商平台等開放給開發者使用的 API。

缺點:

  • 過度或不足取得資料(Over-fetching / Under-fetching):客戶端可能會獲取過多或過少的數據,例如需要 nameprice,卻只能獲得整個 product 物件。
  • 難以處理複雜關聯查詢:當客戶端需要一次性獲取多個相關資源時,可能需要多次請求或額外的 /products?include=reviews 這類參數。

補充:REST vs RPC:關鍵差異與設計哲學

REST 和 RPC 的主要差異在於 它們如何組織 API,以及 它們如何讓客戶端與伺服器溝通

📌 RPC(Remote Procedure Call):像打電話執行函數
RPC 的設計哲學

RPC 把 API 設計成「遠端函數調用」,就像在本地執行一個函數一樣。

當客戶端想要執行某個動作時,只需要呼叫對應的方法名稱並傳入參數,伺服器就會執行該方法並回傳結果。

RPC 的 API 請求格式

客戶端的請求通常是 一個方法名稱 + 參數,例如:

POST /api
Content-Type: application/json

{
  "method": "getProduct",
  "params": { "id": 123 }
}

🔹 這就像是在「打電話給客服」,直接告訴對方你想要做什麼,例如:「請幫我查詢商品 123!」

RPC 的特性
  • 以「動作」(Action)為核心:API 的請求本質上就是呼叫某個函數,如 getProduct()calculateShipping()
  • 不關心「資源」的概念,只在乎執行什麼動作。
  • 方法名稱是 API 的核心,不像 REST 會使用 URL 來描述資源。
📌 REST(Representational State Transfer):像寫信請求資源
REST 的設計哲學

REST 的核心理念是 「萬物皆資源」(Everything is a Resource)

在 REST API 中,每個 API 端點代表一種「資源」(Resource),例如 商品、使用者、訂單 等,而不是函數或方法。客戶端透過 HTTP 方法(GET、POST、PUT、DELETE)來對這些資源進行操作。

REST 的 API 請求格式

如果我們要查詢商品 123,REST API 會這樣設計:

GET /products/123

🔹 這就像是「寫信給某個部門」,請求獲取某個資源,例如:「請寄給我商品 123 的資訊!」

REST 的特性
  • 以「資源」(Resource)為核心,而非函數調用。
  • 使用標準 HTTP 方法來操作資源
    • GET /products/123 → 取得商品 123 的資訊
    • POST /products → 新增一個商品
    • PUT /products/123 → 更新商品 123 的資訊
    • DELETE /products/123 → 刪除商品 123
  • 無狀態(Stateless):每次請求都是獨立的,伺服器不會記住客戶端的狀態。
  • 統一介面(Uniform Interface):使用標準的 HTTP 方法與狀態碼(如 200、404、500)。

📌 RPC vs REST:具體對比
特色RPCREST
核心概念呼叫函數(動作導向)存取資源(資源導向)
請求方式透過 POST,傳送方法名稱和參數透過標準 HTTP 方法(GET、POST、PUT、DELETE)
URL 設計通常是 /api,方法名稱放在請求體內URL 直接表示資源,如 /products/123
擴展性方法越多,API 變得越複雜(需管理大量函數)結構化設計,適合長期擴展
適用場景內部系統、微服務架構公開 API、標準化服務
📌 簡單比喻:打電話 vs 寫信
RPCREST
像是打電話給客服,下指令:「幫我查詢商品 123!」寫信給公司請求資訊:「請提供商品 123 的詳細資料。」
請求方式直接告訴伺服器要執行哪個函數透過 URL 表示資源,請求對該資源的操作
擴展性API 變大時會變得混亂API 變大時仍然有結構

📌 什麼時候選擇 RPC?什麼時候選擇 REST?

選 RPC 當:

  • 內部系統,如微服務之間的通訊。
  • 需要高效能、低延遲的 API(例如 gRPC)。
  • 主要在做函數級的操作,而非管理資源。

選 REST 當:

  • 需要設計 標準化、可公開的 API(例如社群平台、電商 API)。
  • 需要 API 易於理解、可擴展,並與 HTTP 標準相容。
  • 希望透過 URL 直覺地表示資源(例如 /users/123/orders 代表查詢某用戶的訂單)。

GraphQL:自助餐式的「精準查詢」

核心理念

GraphQL 由 Facebook 於 2015 年開源,其核心理念是讓客戶端自行定義所需的數據結構,避免過度獲取(Over-fetching)或不足獲取(Under-fetching)問題。

GraphQL 的特性

  • 單一入口(Single Endpoint):所有請求都透過 /graphql 端點發送。
  • 客戶端決定請求內容,避免獲取不必要的數據。
  • 強型別系統:需要定義 Schema 來描述 API 結構,確保請求與回應的一致性。
  • 支援即時更新(Subscription),適合即時應用,如聊天系統或動態消息。

GraphQL API 查詢範例

query {
  product(id: 123) {
    name
    price
  }
}

GraphQL 回應範例

{
  "data": {
    "product": {
      "name": "無線鍵盤",
      "price": 1990
    }
  }
}

優缺點

優點:

  • 精準查詢:客戶端只獲取所需的數據,減少不必要的數據傳輸。
  • 適合複雜關聯數據:可一次請求獲取多個相關資源,減少多次 API 呼叫的負擔。
  • 靈活性高:適用於不同裝置(如手機、平板、桌機)需要不同數據的場景。

缺點:

  • 學習曲線較高:相較於 REST,開發者需要學習 GraphQL 語法與 Schema 定義。
  • 快取較難:由於所有請求都發送到 /graphql,不如 REST API 容易利用瀏覽器與 CDN 快取。

補充:REST vs GraphQL:關鍵差異與設計哲學

REST 和 GraphQL 都是 API 設計的方式,但它們的核心理念與數據請求方式截然不同。

REST 強調資源導向,透過固定的 API 端點(Endpoint)取得數據,而 GraphQL 則是查詢導向,允許客戶端自由定義所需的數據格式。

📌 REST(Representational State Transfer):以資源為中心的 API 設計
REST 的設計哲學

REST 把 API 視為一組資源(Resource),每個資源都有一個固定的 URL,並透過 HTTP 方法(GET、POST、PUT、DELETE)來操作它們。

REST 的 API 設計範例

假設我們要查詢商品(Product)的資訊,在 REST API 中,請求方式如下:

GET /products/123

如果我們同時想獲取商品的評論(Reviews),可能需要額外的 API 請求:

GET /products/123/reviews

🔹 REST 會根據資源類型提供不同的端點,每個端點回傳的資料格式是固定的,開發者無法選擇回傳哪些欄位。

REST 的特性
  • 以「資源」(Resource)為核心,每個 API 端點對應一個資源類型。
  • 使用固定的 HTTP 方法(GET、POST、PUT、DELETE)來操作資源。
  • 無狀態(Stateless):每次請求都是獨立的,不依賴伺服器的會話狀態。
  • 統一介面(Uniform Interface):不同的 API 遵循一致的設計原則,例如使用 GET 來取得資料,而不是 POST
📌 GraphQL:以查詢為中心的 API 設計
GraphQL 的設計哲學

GraphQL 由 Facebook 在 2015 年開源,它的核心理念是讓客戶端可以精確查詢所需的資料,避免獲取過多或過少的數據。

GraphQL 的 API 設計範例

在 GraphQL 中,客戶端可以用 一個查詢請求(Query) 取得所需的商品資訊和評論:

query {
  product(id: 123) {
    name
    price
    reviews {
      rating
      comment
    }
  }
}

GraphQL 伺服器只會回傳客戶端要求的欄位,例如:

{
  "data": {
    "product": {
      "name": "無線鍵盤",
      "price": 1990,
      "reviews": [
        { "rating": 5, "comment": "超好用!" },
        { "rating": 4, "comment": "不錯,但有點貴。" }
      ]
    }
  }
}

🔹 GraphQL 允許客戶端自由選擇需要的欄位,不會像 REST 一樣回傳固定的資料格式。

GraphQL 的特性
  • 以「查詢」(Query)為核心,客戶端可以指定所需的資料欄位。
  • 單一端點(Single Endpoint):所有請求都發送到 /graphql,而不是像 REST 一樣使用多個端點(如 /products/users)。
  • 避免過度或不足取得資料(Over-fetching / Under-fetching):客戶端可以精準控制回傳的欄位內容,減少不必要的數據傳輸。
  • 支援關聯查詢(Nested Queries):可以一次請求獲取多個相關資源的數據,而不需要發送多個 API 請求。
  • 強型別系統(Schema):需要先定義 API 的數據結構(Schema),確保請求與回應的數據格式一致。
📌 REST vs GraphQL:具體對比
特性RESTGraphQL
核心概念以「資源」(Resource)為中心以「查詢」(Query)為中心
端點設計多個端點(如 /products/123、/users/456)單一端點 /graphql
請求方式GET /products/123 取得商品資訊query { product(id: 123) { name price } }
數據回傳固定的欄位,可能包含不需要的數據客戶端指定所需的欄位,精準回傳
關聯數據查詢需要多個 API 請求(如 /products/123/reviews)可在單一查詢中獲取關聯數據
快取機制瀏覽器和 CDN 可透過 URL 快取 GET 請求由於使用 POST,無法直接快取
適用場景標準化 API(如電商 API、社群平台 API)需要高度靈活性(如行動裝置、多端應用)
📌 簡單比喻:固定菜單 vs 自助餐
RESTGraphQL
像是固定菜單的餐廳:菜單上有固定的菜色(API 回傳固定的欄位)。自助餐:客戶可以自由選擇自己想吃的東西(客戶端決定回傳哪些欄位)。
請求方式每個資源都有獨立的端點,如 /products/123、/users/456只有一個 /graphql 端點,客戶端決定查詢內容
數據回傳伺服器回傳完整的資料,即使客戶端只需要其中一部分客戶端只獲取自己想要的欄位,避免浪費帶寬
關聯數據可能需要多次請求(如 /products/123/reviews)可以一次獲取所有關聯數據
適合場景標準化 API,易於快取與管理需要靈活查詢的應用,如儀表板、行動應用

📌 什麼時候選擇 REST?什麼時候選擇 GraphQL?

選 REST 當:

  • API 需要標準化,並且對不同的客戶端回傳相同的資料格式。
  • 快取(Caching)是重點,例如新聞網站、靜態內容等。
  • 系統簡單,數據關聯性不強,例如電商 API、社群網站 API。

選 GraphQL 當:

  • 客戶端需求多變,例如不同裝置(手機、平板、桌機)需要不同的欄位。
  • 數據關聯性高,需要一次查詢獲取多個相關資源(如 Facebook 動態消息)。
  • 需要減少 API 請求數量,提升效能(如行動應用)。

API 設計選擇指南

特性RPCRESTGraphQL
核心概念動作(Action)資源(Resource)查詢(Query)
請求方法多數用 POST符合 HTTP 方法只用 POST 或 GET
適用場景內部系統、微服務公開 API、標準化系統需要精準查詢、關聯數據

如何選擇?

選 RPC內部微服務、高效能函數調用
選 REST標準化 API、可快取的系統
選 GraphQL客戶端需求多變、複雜數據查詢


實戰範例:用 Python 體驗三種風格

RPC 風格實作(Flask 框架)

from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/api', methods=['POST'])
def handle_rpc():
    data = request.json
    if data['method'] == 'get_product':
        return jsonify({"result": {"id": 123, "name": "鍵盤"}})
    else:
        return jsonify({"error": "方法不存在"})

REST 風格實作(FastAPI 框架)

from fastapi import FastAPI

app = FastAPI()

products = {123: {"name": "鍵盤", "price": 1990}}

@app.get("/products/{product_id}")
def get_product(product_id: int):
    return products.get(product_id)

GraphQL 風格實作(Ariadne 套件)

from ariadne import QueryType, make_executable_schema
from ariadne.asgi import GraphQL

type_defs = """
    type Query {
        product(id: ID!): Product
    }
    type Product {
        id: ID!
        name: String!
        price: Int!
    }
"""

query = QueryType()

@query.field("product")
def resolve_product(*_, id):
    return {"id": id, "name": "鍵盤", "price": 1990}

schema = make_executable_schema(type_defs, query)
app = GraphQL(schema)

總結:沒有最佳解,只有最合適的解

從 XML 到 JSON,從 RPC 到 GraphQL,API 設計的演變反映了軟體開發的核心需求:在結構與彈性之間取得平衡

作為新手,不必追求「最新潮」的技術,而應:

  1. 理解每種風格的適用場景
  2. 根據團隊規模與專案需求選擇
  3. 保持介面設計的一致性

Similar Posts