GraphQL 伺服器架構指南:初學者入門

Published March 19, 2025 by 徐培鈞
架構

當你剛接觸 GraphQL 時,可能會被它的查詢語法所吸引:

你可以一次查多個欄位、不必再開多個 API、甚至還能只取用你要的資料欄位,看起來非常高效又簡潔。

再進一步學習後,你可能已經理解了幾個核心名詞:

Schema 定義查詢能要什麼資料、Resolver 負責回傳資料、Query 是前端送出的請求。

但當你開始動手實作,或遇到跨團隊協作時,常會出現一個困惑:

「這些東西到底是怎麼串起來的?」

「為什麼我寫了 Schema,但資料沒回來?」

「這個 Resolver 放在哪裡?是誰在呼叫它?」

這些問題其實都來自於:

你對於 GraphQL 背後的伺服器架構與執行流程還不夠清楚。

GraphQL 和 REST 的最大差異,不只是語法上的不同,更是在執行模型上的設計哲學差異

REST 傾向把邏輯集中在後端每一個 Endpoint;

但 GraphQL 則強調彈性、模組化與分層設計,把查詢規格(Schema)與查詢實作(Resolver)做了明確拆分。

這種拆分帶來了極大的彈性,但也提高了理解門檻。

因此,若你想更上一層樓,不再只是「會寫 Query」或「知道什麼是 Resolver」,而是能獨立開發一個完整的 GraphQL API,甚至與前後端團隊順利協作,那麼:

了解 GraphQL 伺服器的整體架構運作方式,就是你必經的關鍵一步。

本篇文章將從最基礎的角度切入,幫助你了解:

  • 一個 GraphQL Server 的核心元件有哪些?
  • Query 是如何從前端一路跑到資料庫,再回傳資料?
  • 手寫伺服器與自動化工具(如 Hasura)有什麼差別?
  • 在大型系統裡,GraphQL Server 要部署在哪裡?(One Server Model vs Gateway Model)

這些觀念不只是幫你「學會 GraphQL」,而是幫你具備開發、部署、選型的判斷力

準備好了嗎?我們從三大核心角色開始。

GraphQL Server 的三大核心角色

在 GraphQL 的世界裡,一個伺服器不只是處理查詢請求而已。

它其實是一個分工明確的系統,主要由三個核心元件構成,分別負責「定義、解析、執行」的工作。

功能說明負責定義 API 的規格,包括你可以查詢什麼資料、有哪些欄位、參數格式、型別結構。例如你可以查詢 User 類型,它包含 id、name、posts 等欄位。
功能說明每個欄位背後的實作邏輯都由 Resolver 處理。Resolver 會根據查詢的參數去資料庫撈資料、處理邏輯、甚至整合外部 API,例如從資料庫查出 user.name。
功能說明是負責協調一切的核心角色,從接收查詢、解析查詢語法、比對 Schema、呼叫對應 Resolver,再組裝結果並回傳。

可以把這三者的關係比喻為:

  • Schema 是 API 的說明書(你可以查什麼)
  • Resolver 是 API 的執行者(資料怎麼來)
  • 執行引擎是總指揮官(誰該做什麼、結果怎麼整合)

整體資料流程圖:Query 是怎麼跑的?

當你學會寫出以下這樣的 GraphQL 查詢語法時:

query {
  user(id: "123") {
    name
    email
  }
}

你可能會好奇:「這一段文字,實際上是怎麼變成我看到的資料?GraphQL 在背後到底做了什麼?」

事實上,這整個過程包含了多個階段,每個階段都扮演著關鍵角色:

詳細流程解析:GraphQL 查詢從送出到回傳的每一步

當你在前端畫面上點擊按鈕、或寫下這樣的查詢時:

query {
  user(id: "123") {
    name
    email
  }
}

你其實啟動了一段完整的查詢生命週期。這段看起來簡單的文字,會經歷以下五大階段,才會變成畫面上你看到的資料。

flowchart TD
    A[Client] -->|送出查詢| B[GraphQL Server]
    B -->|執行引擎| C[Schema]
    C -->|比對查詢結構| D[Resolver]
    D -->|逐欄位處理資料獲取| E[資料來源 DB / API / 快取等]
    E -->|執行引擎組合回傳資料| F[JSON 格式]
    F -->|回傳結果| A

1️⃣ 前端送出查詢請求(Query)

查詢可以來自:

  • 使用者點擊頁面上的按鈕,觸發 React 或 Vue 等框架裡的 API 請求
  • 開發者在程式中呼叫 Apollo Client、Relay、urql 等工具庫
  • Postman 或 GraphQL Playground 等工具的手動測試

這些工具最終都會將查詢內容(例如上面的 user(id: "123"))以 HTTP 請求(通常是 POST /graphql)的形式送到 GraphQL Server。

✅ 查詢內容的意義

這段查詢代表的意思是:

「請幫我找出 id = 123 的使用者,然後只回傳他的 nameemail 欄位。」

這就是 GraphQL 的精髓之一:只取你要的資料欄位,其他不拿。

2️⃣ 伺服器收到查詢 → 執行語法解析與 Schema 驗證

伺服器收到這段查詢文字後,並不會立刻查資料,而是先經過兩個重要步驟:

🔹 a. 語法解析(Parsing)

GraphQL Server(如 Apollo Server)會把這段文字解析成一個「抽象語法樹(AST)」結構,就像語言老師幫你分析句子主詞、動詞、受詞一樣,確保這是一個合法的 GraphQL 語句。

🔹 b. Schema 驗證(Validation)

接著會拿這個 AST 跟伺服器中事先定義好的 Schema 進行比對,檢查以下事項:

說明user 是不是 Query 中允許查詢的欄位?name、email 是否是 User 類型中允許的欄位?
說明id 是不是應該是字串(ID)?如果你傳成整數,會報錯。
說明查詢語法有沒有寫錯?花括號 {} 有沒有少寫?欄位是否嵌套正確?
❗ 若驗證失敗會怎樣?

GraphQL 是結構嚴謹且強型別的語言,只要有一個欄位拼錯、漏參數、型別不符,就會直接回傳錯誤訊息給前端,而不會進入查資料的階段。

3️⃣ 對照 Schema → 決定呼叫哪些 Resolver

通過語法解析與驗證後,GraphQL Server 就會開始進行實際查詢的規劃。

這時,它會根據 Schema 決定:

  • 查詢的主體是 Query.user(用 id 作為參數)
  • 回傳的是 User 型別,裡面需要的欄位是 nameemail

你可以把 Schema 想像成 API 的菜單或藍圖,例如:

type Query {
  user(id: ID!): User
}

type User {
  name: String
  email: String
}

從這裡,執行引擎知道該怎麼走接下來的流程——它會開始呼叫對應的 Resolver 來處理這些欄位。

4️⃣ 呼叫 Resolver → 執行查詢邏輯

每個欄位(包括最外層的 user 和內層的 nameemail)都會對應一個 Resolver 函式,這些函式會負責實際的查詢邏輯。

以下是可能的 Resolver 實作範例:

const resolvers = {
  Query: {
    user: (_, { id }) => db.users.findById(id),
  },
  User: {
    name: (parent) => parent.name,
    email: (parent) => parent.email,
  },
};
✅ 重點說明:
功能根據傳入的 id 去資料庫查出對應的使用者物件
功能從 parent(也就是剛剛查到的使用者物件)中取出 name
功能同上,取出 email 欄位

這裡的每個欄位都會被單獨觸發一次 Resolver,這就是為什麼說 GraphQL 是「欄位為單位」執行。

🔎 注意:如果查詢中包含多層巢狀欄位,GraphQL 會依照欄位階層遞迴地觸發 Resolver,層層往下查。

5️⃣ 整合資料 → 回傳 JSON 結果給前端

當所有 Resolver 都查完資料後,GraphQL Server 的執行引擎會將這些欄位的結果整合起來,組裝成一份標準的 JSON 格式回應,例如:

{
  "data": {
    "user": {
      "name": "Alice",
      "email": "alice@example.com"
    }
  }
}

這份結果會透過 HTTP 回傳給前端應用程式,前端拿到這份資料後,就可以:

  • 顯示在畫面上(如使用者個人資料卡)
  • 存入 local state 或 cache
  • 傳遞給其他元件或做後續處理

🎯 小結:這五步驟構成一套完整的資料查詢生命週期

發生位置前端
功能發送指定欄位的查詢
發生位置GraphQL Server
功能確保查詢語法與結構正確
發生位置執行引擎
功能決定要呼叫哪些 Resolver
發生位置資料查詢階段
功能逐欄位查詢資料來源
發生位置執行引擎
功能回傳 JSON 結果給前端

這不僅是 GraphQL 的執行過程,也是一種思考方式:每個欄位都是一個執行單位,Schema 是你的查詢藍圖,而 Resolver 是查詢的工人。

GraphQL Server 的開發方式:手寫 vs 自動化

當你決定採用 GraphQL 作為 API 的解決方案時,第一個實作層級的問題就是:

「我要自己從零寫一個 GraphQL Server,還是用工具幫我產生?」

這不只是寫法風格的不同,而是會深刻影響整體開發流程、架構彈性、權限控制、整合能力與未來的維護方式。

這裡我們來介紹兩種常見的開發策略:

  • 手寫型(自訂 Schema 與 Resolver):擁有最高的控制權與彈性,但需要較多的工程經驗。
  • 自動化型(工具自動產生 API):以資料庫為核心、開發快速,適合追求效率與快速迭代的團隊。

手寫型:Apollo Server + 自訂 Schema / Resolver

這種方式是 GraphQL 最原始、最完整的實作模式,由開發者自行撰寫整個 GraphQL Server 的核心三要素:

  • Schema(定義 API 結構)
  • Resolver(欄位查詢邏輯)
  • 執行引擎設定(如 ApolloServer)

這就像你親手打造一間房子,從藍圖、牆面、管線配置通通自己來,雖然花時間,但能夠完全符合你的需求。

🛠 範例:Hello World GraphQL Server

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => "Hello, world!",
  },
};

const server = new ApolloServer({ typeDefs, resolvers });
server.listen();

這段程式碼完成了最基本的 GraphQL 查詢能力,只要啟動伺服器,就能從前端查詢 hello 欄位並獲得回應。

🔍 拆解這段程式碼在做什麼?

說明與功能使用 gql 這個函式標示 Schema,定義可以查詢哪些欄位
說明與功能存放整體 API 的查詢架構(本例只有一個欄位 hello)
說明與功能對應每個欄位的邏輯實作(例如 hello() 回傳一段文字)
說明與功能整合 Schema 與 Resolver,啟動一個符合 GraphQL 規格的 API

✅ 適合情境

原因Resolver 可以自由控制查資料的邏輯
原因可在 Resolver 加入權限檢查、條件篩選、錯誤處理
原因Schema 可以自由設計、不受資料表限制
原因這種模式能支撐大型應用的系統邏輯

✅ 優缺點整理

說明欄位邏輯、資料來源、授權控制都可自由調整
說明最能幫助你真正理解 GraphQL 是怎麼查資料的
說明特殊欄位轉換、跨表整合、動態權限都可自己實作
說明每個欄位都要手動實作 Resolver
說明需要理解 Schema、Resolver、執行流程等
說明Schema 與資料庫要自己對齊,錯誤處理要自己補

自動化型:Hasura、PostGraphile 等工具

如果你已經有一份資料庫,而且希望快速讓前端可以查資料,或正在開發原型、內部系統、自動化後台,那麼你可能不想花太多時間寫 Resolver。

自動化型的工具就是為這種需求設計的。

它們的核心理念是:

📌「Schema 不需要你寫,我幫你根據資料表自動產生,還附帶基本的 CRUD Resolver。」

🔧 自動化工具運作原理

以 Hasura 為例:

flowchart TD
    A["資料庫表格建立"] -->|"定義資料結構"| B["Hasura 掃描表格與欄位"]
    B -->|"自動探索關聯"| C["自動產生對應的 Query / Mutation"]
    C -->|"即時生成 API"| D["提供可立即使用的 GraphQL API"]

你只要打開網頁後台介面(Hasura Console),建立資料表,就能立即透過 /graphql 查資料。

✅ 適合情境

原因不需寫 API,也不需後端團隊
原因幾分鐘即可完成可用 API
原因表格欄位就是你的 API 規格

✅ 優缺點整理

說明只要有資料庫就能使用
說明Schema、Resolver 省下大量重複工
說明可根據使用者角色設定讀寫範圍
說明資料變動可即時推播(Hasura 特色)
說明複雜邏輯需要額外擴充(Actions、Remote Schema)
說明原生僅支援單一 DB,整合外部 API 較麻煩
說明Schema 變動仰賴 DB 變動,不易抽象拆分邏輯層

小結:你該選哪一種?

手寫型⭐⭐⭐⭐⭐ 完整控制邏輯與資料來源
自動化型⭐⭐ 限於資料庫結構
手寫型⭐⭐ 建立需時
自動化型⭐⭐⭐⭐⭐ 即刻產出 API
手寫型⭐⭐⭐⭐ 理解概念後才能正確實作
自動化型⭐ 容易上手,圖形介面操作
手寫型中大型系統、API 層整合
自動化型原型開發、小型工具、資料查詢介面
手寫型⭐⭐⭐⭐⭐ 彈性高,適合擴充架構
自動化型⭐⭐ 需仰賴工具更新與資料庫設計穩定性

GraphQL Server 在系統中的部署角色:單一伺服器 vs Gateway

當你完成一個 GraphQL Server 的開發,不論是手寫還是自動產生,下一個關鍵問題是:

🔧「我要怎麼把這個 Server 部署到整體系統中?它是獨立應用?還是協調者?」

這就像蓋好一座建築後,要決定它的角色是住宅、商辦,還是轉運中心。

在系統架構裡,我們會根據業務規模、團隊結構與資料來源多樣性,選擇不同的部署模型。最常見的有兩種:

模式一:One Server Model(單一應用伺服器)

在這種模式下,GraphQL Server 就是你的應用核心。它負責:

  • 對外提供 API
  • 對內直接查資料庫
  • 同時處理業務邏輯、驗證、權限等功能

簡單來說:前端查資料 → GraphQL Server → 直接查資料庫 → 回傳結果

📌 架構圖:

flowchart TD
    A[Client] -->|查詢| B[GraphQL Server]
    B -->|資料操作| C[Database / Cache]

你在開發中寫的 Resolver,會直接呼叫 ORM(如 Prisma、Sequelize)或查 SQL,沒有其他中介層。

✅ 特點:

說明單一 Server 即可對外服務
說明不經過中繼層,直連資料庫
說明授權、日誌、驗證機制都能完全掌握
說明所有責任都由一台 Server 負責

✅ 適用情境:

原因架構單純、速度快,快速驗證產品
原因大多查單一資料庫、邏輯簡單
原因前後端由一人或一小組維護,溝通成本低
原因無須考慮服務分拆與整合問題

⚠️ 缺點與挑戰:

說明隨功能增長,Server 腫脹難維護
說明要串多系統(如 CRM、ERP)會變複雜
說明所有人都改同一套 Schema 與 Resolver 容易衝突

模式二:GraphQL Gateway(Two Server Model)

GraphQL Gateway 是一種架構模式,專門用來整合多個子系統的資料來源
它本身不執行業務邏輯,而是作為一層「查詢入口」,把前端送來的查詢拆解後,分別轉給對應的子服務。

📌 概念上,它就像是「API 的中控台」或「資料轉運站」。

📌 架構圖:

flowchart TD
    A[Client] -->|請求| B[GraphQL Gateway]
    B -->|查詢| C[User Service]
    B -->|查詢| D[Order Service]
    B -->|查詢| E[CMS Service]

    C -->|資料操作| F1[Database]
    C -->|資料操作| F2[REST API]
    C -->|資料操作| F3[GraphQL API]
    C -->|資料操作| F4[外部 API]

    D -->|資料操作| F1
    D -->|資料操作| F2
    D -->|資料操作| F3
    D -->|資料操作| F4

    E -->|資料操作| F1
    E -->|資料操作| F2
    E -->|資料操作| F3
    E -->|資料操作| F4

    F1 -->|SQL/NoSQL| G1[資料庫存儲]
    F2 -->|HTTP 請求| G2[RESTful 服務]
    F3 -->|查詢語言| G3[其他 GraphQL 服務]
    F4 -->|API 調用| G4[第三方服務]

每個子系統(Microservice)可以是:

  • REST API
  • GraphQL API
  • gRPC 服務
  • 甚至是第三方服務(如 Stripe、Firebase、Contentful)

特點:

說明各服務各司其職,Gateway 專責整合
說明可依功能獨立擴展服務,維護壓力分散
說明所有資料透過單一 Endpoint 查詢
說明Mobile、Web、BI 可用同一 Gateway API

適用情境:

原因子系統獨立維運,需統一對外 API
原因REST、GraphQL、DB 來源混合
原因每組團隊可擁有自己子 GraphQL Schema
原因Gateway 負責 UI 所需欄位定義與轉換

缺點與挑戰:

說明查詢效率、錯誤傳遞、N+1 問題都需考量
說明多個服務 Schema 命名、版本、欄位定義需協調一致
說明Federation、Stitching 等技術學習門檻略高

結語:理解架構,才能靈活應用

如果說「Schema 是合約、Resolver 是執行人」,那整個 GraphQL Server 就是協助雙方溝通的舞台。

你現在已經了解:

  • Schema 與 Resolver 在伺服器中的角色
  • 手寫 vs 自動化 的開發方式
  • One Server vs Gateway 的部署差異
  • 為何要根據專案規模與資料來源選擇架構

這些認知將幫助你:

  • 正確設計查詢結構
  • 避免效能陷阱(如 N+1 問題)
  • 評估是否該引入 Hasura、Codegen 等工具