當你剛接觸 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 的世界裡,一個伺服器不只是處理查詢請求而已。
它其實是一個分工明確的系統,主要由三個核心元件構成,分別負責「定義、解析、執行」的工作。
| 元件 | 功能說明 |
|---|---|
| Schema | 負責定義 API 的規格,包括你可以查詢什麼資料、有哪些欄位、參數格式、型別結構。例如你可以查詢 User 類型,它包含 id、name、posts 等欄位。 |
| Resolver | 每個欄位背後的實作邏輯都由 Resolver 處理。Resolver 會根據查詢的參數去資料庫撈資料、處理邏輯、甚至整合外部 API,例如從資料庫查出 user.name。 |
| 執行引擎(例如 ApolloServer) | 是負責協調一切的核心角色,從接收查詢、解析查詢語法、比對 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 -->|回傳結果| A1️⃣ 前端送出查詢請求(Query)
查詢可以來自:
- 使用者點擊頁面上的按鈕,觸發 React 或 Vue 等框架裡的 API 請求
- 開發者在程式中呼叫 Apollo Client、Relay、urql 等工具庫
- Postman 或 GraphQL Playground 等工具的手動測試
這些工具最終都會將查詢內容(例如上面的 user(id: "123"))以 HTTP 請求(通常是 POST /graphql)的形式送到 GraphQL Server。
✅ 查詢內容的意義
這段查詢代表的意思是:
「請幫我找出
id = 123的使用者,然後只回傳他的name和
這就是 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型別,裡面需要的欄位是name和email
你可以把 Schema 想像成 API 的菜單或藍圖,例如:
type Query {
user(id: ID!): User
}
type User {
name: String
email: String
}從這裡,執行引擎知道該怎麼走接下來的流程——它會開始呼叫對應的 Resolver 來處理這些欄位。
4️⃣ 呼叫 Resolver → 執行查詢邏輯
每個欄位(包括最外層的 user 和內層的 name、email)都會對應一個 Resolver 函式,這些函式會負責實際的查詢邏輯。
以下是可能的 Resolver 實作範例:
const resolvers = {
Query: {
user: (_, { id }) => db.users.findById(id),
},
User: {
name: (parent) => parent.name,
email: (parent) => parent.email,
},
};✅ 重點說明:
| Resolver | 功能 |
|---|---|
Query.user | 根據傳入的 id 去資料庫查出對應的使用者物件 |
User.name | 從 parent(也就是剛剛查到的使用者物件)中取出 name |
User.email | 同上,取出 email 欄位 |
這裡的每個欄位都會被單獨觸發一次 Resolver,這就是為什麼說 GraphQL 是「欄位為單位」執行。
🔎 注意:如果查詢中包含多層巢狀欄位,GraphQL 會依照欄位階層遞迴地觸發 Resolver,層層往下查。
5️⃣ 整合資料 → 回傳 JSON 結果給前端
當所有 Resolver 都查完資料後,GraphQL Server 的執行引擎會將這些欄位的結果整合起來,組裝成一份標準的 JSON 格式回應,例如:
{
"data": {
"user": {
"name": "Alice",
"email": "alice@example.com"
}
}
}這份結果會透過 HTTP 回傳給前端應用程式,前端拿到這份資料後,就可以:
- 顯示在畫面上(如使用者個人資料卡)
- 存入 local state 或 cache
- 傳遞給其他元件或做後續處理
🎯 小結:這五步驟構成一套完整的資料查詢生命週期
| 階段 | 發生位置 | 功能 |
|---|---|---|
| 1. 查詢送出 | 前端 | 發送指定欄位的查詢 |
| 2. 語法與驗證 | GraphQL Server | 確保查詢語法與結構正確 |
| 3. 對照 Schema | 執行引擎 | 決定要呼叫哪些 Resolver |
| 4. 呼叫 Resolver | 資料查詢階段 | 逐欄位查詢資料來源 |
| 5. 組合結果 | 執行引擎 | 回傳 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\…“ | 使用 gql 這個函式標示 Schema,定義可以查詢哪些欄位 |
typeDefs | 存放整體 API 的查詢架構(本例只有一個欄位 hello) |
resolvers | 對應每個欄位的邏輯實作(例如 hello() 回傳一段文字) |
ApolloServer | 整合 Schema 與 Resolver,啟動一個符合 GraphQL 規格的 API |
✅ 適合情境
| 適合情境 | 原因 |
|---|---|
| 需要串接多個資料來源(SQL、REST、Firebase、第三方 API) | Resolver 可以自由控制查資料的邏輯 |
| 有特殊授權/驗證/商業邏輯需求 | 可在 Resolver 加入權限檢查、條件篩選、錯誤處理 |
| 想建立自定義查詢語法或複雜資料模型 | Schema 可以自由設計、不受資料表限制 |
| 需要高度擴充性或整合架構(如 BFF、Gateway) | 這種模式能支撐大型應用的系統邏輯 |
✅ 優缺點整理
| 優點 | 說明 |
|---|---|
| 彈性極高 | 欄位邏輯、資料來源、授權控制都可自由調整 |
| 學習效果佳 | 最能幫助你真正理解 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 查資料。
✅ 適合情境
| 適合情境 | 原因 |
|---|---|
| 想快速建立 MVP、內部管理後台、BI 工具 | 不需寫 API,也不需後端團隊 |
| 專案開發初期,希望盡快驗證資料流或 UI 整合 | 幾分鐘即可完成可用 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 負責 |
✅ 適用情境:
| 適用場景 | 原因 |
|---|---|
| 新創產品或 MVP | 架構單純、速度快,快速驗證產品 |
| 管理後台系統 | 大多查單一資料庫、邏輯簡單 |
| 全端開發 | 前後端由一人或一小組維護,溝通成本低 |
| 尚未使用微服務 | 無須考慮服務分拆與整合問題 |
⚠️ 缺點與挑戰:
| 問題 | 說明 |
|---|---|
| 容易變成單點瓶頸 | 隨功能增長,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 所需欄位定義與轉換 |
缺點與挑戰:
| 問題 | 說明 |
|---|---|
| Gateway 本身要設計得好 | 查詢效率、錯誤傳遞、N+1 問題都需考量 |
| 需要協定管理 | 多個服務 Schema 命名、版本、欄位定義需協調一致 |
| 初期建置較複雜 | Federation、Stitching 等技術學習門檻略高 |
結語:理解架構,才能靈活應用
如果說「Schema 是合約、Resolver 是執行人」,那整個 GraphQL Server 就是協助雙方溝通的舞台。
你現在已經了解:
- Schema 與 Resolver 在伺服器中的角色
- 手寫 vs 自動化 的開發方式
- One Server vs Gateway 的部署差異
- 為何要根據專案規模與資料來源選擇架構
這些認知將幫助你:
- 正確設計查詢結構
- 避免效能陷阱(如 N+1 問題)
- 評估是否該引入 Hasura、Codegen 等工具