當你剛接觸 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 的說明書(你可以查什麼)
- 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 進行比對,檢查以下事項:
❗ 若驗證失敗會怎樣?
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,這就是為什麼說 GraphQL 是「欄位為單位」執行。
🔎 注意:如果查詢中包含多層巢狀欄位,GraphQL 會依照欄位階層遞迴地觸發 Resolver,層層往下查。
5️⃣ 整合資料 → 回傳 JSON 結果給前端
當所有 Resolver 都查完資料後,GraphQL Server 的執行引擎會將這些欄位的結果整合起來,組裝成一份標準的 JSON 格式回應,例如:
{
"data": {
"user": {
"name": "Alice",
"email": "alice@example.com"
}
}
}這份結果會透過 HTTP 回傳給前端應用程式,前端拿到這份資料後,就可以:
- 顯示在畫面上(如使用者個人資料卡)
- 存入 local state 或 cache
- 傳遞給其他元件或做後續處理
🎯 小結:這五步驟構成一套完整的資料查詢生命週期
這不僅是 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 欄位並獲得回應。
🔍 拆解這段程式碼在做什麼?
✅ 適合情境
✅ 優缺點整理
自動化型:Hasura、PostGraphile 等工具
如果你已經有一份資料庫,而且希望快速讓前端可以查資料,或正在開發原型、內部系統、自動化後台,那麼你可能不想花太多時間寫 Resolver。
自動化型的工具就是為這種需求設計的。
它們的核心理念是:
📌「Schema 不需要你寫,我幫你根據資料表自動產生,還附帶基本的 CRUD Resolver。」
🔧 自動化工具運作原理
以 Hasura 為例:
flowchart TD
A["資料庫表格建立"] -->|"定義資料結構"| B["Hasura 掃描表格與欄位"]
B -->|"自動探索關聯"| C["自動產生對應的 Query / Mutation"]
C -->|"即時生成 API"| D["提供可立即使用的 GraphQL API"]你只要打開網頁後台介面(Hasura Console),建立資料表,就能立即透過 /graphql 查資料。
✅ 適合情境
✅ 優缺點整理
小結:你該選哪一種?
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,沒有其他中介層。
✅ 特點:
✅ 適用情境:
⚠️ 缺點與挑戰:
模式二: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)
特點:
適用情境:
缺點與挑戰:
結語:理解架構,才能靈活應用
如果說「Schema 是合約、Resolver 是執行人」,那整個 GraphQL Server 就是協助雙方溝通的舞台。
你現在已經了解:
- Schema 與 Resolver 在伺服器中的角色
- 手寫 vs 自動化 的開發方式
- One Server vs Gateway 的部署差異
- 為何要根據專案規模與資料來源選擇架構
這些認知將幫助你:
- 正確設計查詢結構
- 避免效能陷阱(如 N+1 問題)
- 評估是否該引入 Hasura、Codegen 等工具