初學者入門:認識 GraphQL Schema
更新日期: 2025 年 4 月 30 日
如果你正在學習 GraphQL, 一定會很快遇到一個超級重要的關鍵字:Schema(綱要、結構)。
Schema 是 GraphQL 的骨架。
它定義了:
- 後端有哪些資料可以提供?
- 這些資料是什麼型別?
- 前端可以發送哪些查詢、變更請求?
- 每個操作需要帶哪些參數、會回傳什麼?
簡單來說,沒有 Schema,就沒有 GraphQL!
Schema 是前後端溝通的「共同語言」,也是 GraphQL 能做到高效、嚴謹開發的核心原因。
接下來,我們會一步步帶你了解:
- 什麼是 GraphQL Schema?
- Schema 裡面有哪些基本型別?
- Schema 是如何幫助我們查資料、改資料?
GraphQL Schema 是由兩大系統組成的
當我們說 GraphQL 是「Schema 驅動」的,其實指的是整個系統的運作邏輯,是由兩大核心系統共同構成的。
這兩大系統分別是:
系統名稱 | 功能定位 | 常見項目 |
---|---|---|
🧭 操作系統 | 定義前端可以對系統做哪些事 | Query、Mutation、Subscription |
🧱 型別系統 | 定義系統中資料長什麼樣子(結構與型別) | Object、Scalar、Input、Enum、Interface 等 |
這兩者缺一不可。
一個是「可以做什麼事」,一個是「這些事會用到什麼資料格式」,共同構成完整的 GraphQL 生態。
操作系統:告訴前端「可以做哪些操作」
這部分是 Schema 的「功能層」,它定義了前端可以發起哪些請求。
你可以這樣理解:
Query
➜ 查資料(像是查一篇文章)Mutation
➜ 改資料(像是新增留言)Subscription
➜ 即時訂閱資料(像是聊天室新訊息)
這些操作就像是你走進一間餐廳時,菜單上列出的動作:
「我可以點餐」、「我可以取消訂單」、「我可以訂閱外送進度」
Schema 就是那份菜單,而操作系統就是讓前端知道這些服務有哪些「入口點」可以呼叫。
型別系統:告訴大家「資料長什麼樣子」
而型別系統才是真正的「資料說明書」,它負責定義所有資料的格式與結構。
讓操作系統能正確驗證每一筆請求該怎麼傳資料、該回傳什麼資料。
包括:
型別 | 功能說明 |
---|---|
Object Type | 定義一筆資料物件有哪些欄位 |
Scalar Type | 最基本的資料型別(字串、整數、布林等) |
Input Type | 專門用來傳入參數(例如:表單送出格式) |
Enum Type | 固定選項的欄位限制(例如角色只能是 ADMIN/USER) |
你可以把這些型別想像成餐廳裡的「菜品內容」:
「這道菜需要哪些材料?食材是什麼型態?有沒有選項可選?」
換句話說,操作系統是告訴你「你可以點菜」,型別系統則是告訴你「每道菜怎麼做、包含什麼內容」。
綜合比喻:GraphQL 就像一間餐廳
角色 | 對應系統 | 解釋 |
---|---|---|
菜單(服務流程) | 操作系統 | 前端可以點哪些服務(查資料、改資料、追蹤) |
每道菜的配方 | 型別系統 | 每筆資料的欄位格式、型別與限制 |
廚房(資料來源) | Resolver + DB | 根據操作與型別,把資料組出來回傳前端 |
你不能只有菜單而不知道菜怎麼做,也不能只知道食材卻不知道能點什麼。這兩個系統要一起協作,前端請求才會成立、後端才能正確回應。
三大核心操作類型:Query、Mutation、Subscription
在 GraphQL 中,一切的操作都要明確地在 Schema 中定義出來。
不論你是要查資料、改資料、還是接收即時更新,都需要透過三種核心操作類型(Operation Types)來進行:
類型 | 功能說明 | 常見用途 |
---|---|---|
Query | 讀取資料 | 查詢使用者資料、取得文章清單、檢視商品資訊 |
Mutation | 修改資料(新增、刪除、更新) | 建立留言、更新個人設定、刪除商品 |
Subscription | 訂閱即時資料變化 | 新留言通知、聊天室訊息、訂單出貨狀態變更 |
Query:查資料的主力工具
Query
是最常用的操作類型,主要用途是「讀取」資料,也就是從後端系統(或資料庫)取出現有的資訊給前端使用。
範例:
query {
getUser(id: "123") {
id
name
email
}
}
這段代表查詢 ID 為 123 的使用者,並要求回傳其 id
、name
、email
。
✅ 特點與設計哲學:
- 不會改變後端資料:單純讀取,不會有任何副作用。
- 可多次重複發送:你可以一直查詢同一筆資料,不需要擔心資料狀態改變。
- 對應 REST 中的 GET 請求。
🧠 初學者常見誤解:
❌「Query 好像也可以改資料吧?」
➜ 錯,GraphQL 嚴格區分讀寫操作。所有改資料的行為都必須使用 Mutation,即使只是切換一個布林值也不例外。
Mutation:改資料就靠它
Mutation
是用來執行「改變系統內部狀態」的操作,包括新增、刪除、修改資料。
和 Query 不同,Mutation 通常具有副作用(side effect),會真正地改變資料庫中的內容。
範例:
mutation {
createPost(input: { title: "新文章", content: "內容" }) {
id
title
}
}
這段代表新增一篇文章,並回傳它的 id
和 title
。
✅ 特點與設計哲學:
- 每個 Mutation 通常只處理一個「動作」(例如:
createPost
、updateUser
) - 可以定義輸入與回傳的資料格式(透過
input
type 與回傳的type
) - 對應 REST 中的 POST、PUT、DELETE 請求
🧠 初學者常見誤解:
❌「Mutation 一定只能處理單筆嗎?」
➜ 不一定,一個 Mutation 也可以操作多筆資料,或同時觸發多個邏輯(但要注意設計清晰、資料一致性)
Subscription:實現即時互動體驗
Subscription
是一種即時通訊機制,允許前端訂閱某個事件,只要後端有新資料更新,就會主動推送結果給前端。
範例:
subscription {
messageAdded {
id
content
sender
}
}
這段代表:前端訂閱聊天室中新的訊息,一旦有人發送新訊息,後端就會自動通知前端。
✅ 特點與設計哲學:
- 基於 WebSocket 建立持續連線(不像 Query/Mutation 是單次請求)
- 非常適合做聊天室、通知系統、直播互動、狀態追蹤
- 前端不需要輪詢,也不需要自己刷新畫面
🧠 初學者常見誤解:
❌「Subscription 就是 Mutation + WebSocket 嗎?」
➜ 錯,Subscription 不會主動改資料,它只是監聽資料變化或後端事件,由後端根據觸發條件主動推送。
實務設計補充:這三種操作可以怎麼搭配?
以「留言系統」為例,三種操作會這樣合作:
功能 | 操作類型 | 說明 |
---|---|---|
查詢所有留言 | Query | 取得目前留言清單 |
新增留言 | Mutation | 將一筆新留言寫入資料庫 |
即時推播留言 | Subscription | 當其他人留言時,前端自動收到更新資訊 |
🧱 每一個 Schema 至少要有一個 Query
根據 GraphQL 的設計規範:
- 每個 Schema 一定要有一個根層級的
Query
,否則你就什麼資料都查不到。 Mutation
和Subscription
則是選配的,只有當你要「改資料」或「做即時互動」時才會定義。
✅ 如果你只是查靜態資料(例如部落格文章、FAQ 清單),可以只寫 Query 就夠用了。
小結:三種操作是 Schema 的核心骨架
類型 | 操作性質 | 功能範圍 | 是否改資料 | 使用頻率 |
---|---|---|---|---|
Query | 讀取 | 查資料 | ❌ 否 | 🟢 非常頻繁 |
Mutation | 寫入 | 增刪改資料 | ✅ 是 | 🟡 常見但慎用 |
Subscription | 被動接收 | 即時互動 | ❌ 否 | 🔵 有需求才用 |
GraphQL 的設計哲學非常清楚:
❝ 把讀與寫分開、把即時互動也分出來,讓前端能清楚表達需求,後端能穩定回應處理。❞
Schema 型別與資料庫的關係
在設計 GraphQL Schema 時,我們會用各種型別來描述資料的結構。
這些型別不只讓前端知道可以查什麼資料,也幫助後端正確處理請求。
接下來,我們會一一介紹這些型別,並說明它們跟資料庫之間的關係。
Object Type(物件型別) ➜ 對應到「資料表裡的一筆資料」
Object Type 是 Schema 裡最基本的單位,代表一種「資料的形狀」。
例如,定義一個 User
型別:
type User {
id: ID!
name: String!
email: String!
}
這表示:
User
是一種物件,裡面有id
、name
、email
三個欄位。- 每個欄位都指定了型別,例如
ID!
表示這是必填的 ID。 - 前端查詢時,只能取得這裡定義過的欄位。
💡 那它和資料庫有什麼關係?
雖然 GraphQL 並不直接操作資料庫。
但在大多數應用中,User
這個型別會對應到資料庫中的一個 users
資料表。
而一筆筆的 User
資料,就對應到 users
表裡的一行(row)。
GraphQL Type | 資料庫概念 |
---|---|
type User | users 資料表的結構 |
一個 User | users 表中的一筆資料 |
✅ 你可以這樣記:Object Type 是「描述資料的外觀」,資料表是「實際儲存資料的地方」。
Scalar Type(標量型別) ➜ 對應到「欄位的資料型別」
Scalar Type 就是最基本的資料型別,用來描述欄位的內容是什麼類型。
GraphQL 提供了以下幾種標準型別:
型別 | 說明 | 常見資料庫對應類型 |
---|---|---|
Int | 整數(例如:1, 100) | INTEGER、BIGINT |
Float | 浮點數(例如:3.14) | FLOAT、DECIMAL |
String | 字串(例如:”hello”) | VARCHAR、TEXT |
Boolean | 布林值(true 或 false) | BOOLEAN、TINYINT(1) |
ID | 唯一識別碼(通常是字串或數字) | UUID、INT、VARCHAR |
✅ GraphQL 的 Scalar Type 有點像是「欄位型別」,幫助我們控制每個欄位該放什麼資料。
Input Type(輸入型別) ➜ 對應到「送進資料庫的參數格式」
當你想要新增或修改一筆資料時(例如送出表單)。
就會需要一個清楚定義好的「輸入格式」,這就是 Input Type 的功能。
範例:
input CreatePostInput {
title: String!
content: String!
}
然後在 Mutation 中這樣使用:
type Mutation {
createPost(input: CreatePostInput!): Post
}
💡 那它和資料庫有什麼關係?
這些 input
欄位,其實就是未來準備寫入資料庫時的資料格式。
例如 CreatePostInput
可能會對應到資料表 posts
中的 title
與 content
欄位。
你可以這樣比喻:
GraphQL Input Type | 資料庫操作 |
---|---|
CreatePostInput | 建立一筆新文章資料的欄位輸入格式 |
✅ Input Type 幫你提前驗證輸入資料,確保送進後端的資料是正確、格式一致的。
Enum Type(列舉型別) ➜ 對應到「固定值欄位的限制」
當你有一個欄位,只能接受幾個固定值時,就可以使用 enum
來強制限制。
範例:
enum Role {
ADMIN
USER
GUEST
}
當你在 User
裡定義這樣的欄位:
type User {
id: ID!
name: String!
role: Role!
}
就代表:這個使用者的角色只能是 ADMIN
、USER
、或 GUEST
。
💡 那它和資料庫有什麼關係?
在資料庫中,我們常會用:
ENUM
欄位(如果資料庫支援),或VARCHAR
加上後端檢查
來實作這種「只允許特定值」的欄位。
GraphQL Enum Type | 資料庫對應方式 |
---|---|
enum Role | ENUM 型別或 VARCHAR 搭配驗證機制 |
✅ Enum Type 可以讓前端查資料時更有信心,也讓後端驗證資料更嚴謹。
GraphQL 四大型別 (Type) 的關聯
我們可以用一個比喻來理解:
✅ 把 GraphQL Schema 想像成一間「餐廳系統」
每種 Type 都扮演不同角色,但彼此合作完成一筆資料的查詢/變更流程。
角色總覽表
種類 | 用來描述… | 主要出現在哪裡? | 用途說明 |
---|---|---|---|
Object Type | 真正的資料物件 | Query / Mutation 的回傳值 | 描述「資料的長相」 |
Scalar Type | 欄位的基本型別 | Object / Input 裡面的欄位 | 定義欄位的「資料型別」 |
Input Type | 輸入參數的格式 | Mutation 或查詢參數 | 用來「包裝一組參數」 |
Enum Type | 固定選項的清單 | Object / Input 裡面的欄位 | 強制欄位只能使用特定值 |
彼此的關係(具體圖解)
我們可以這樣畫出一個邏輯流程:
flowchart TD subgraph "Object Type" User["User (Object Type)"] User --> UserID["id: ID!"] User --> UserName["name: String!"] User --> UserRole["role: Role!"] end subgraph "Scalar Type" ID["ID!"] String["String!"] end subgraph "Enum Type" Role["Role!"] end subgraph "Input Type: CreateUserInput" CreateUserInput["CreateUserInput"] CreateUserInput --> InputName["name: String!"] CreateUserInput --> InputRole["role: Role!"] end subgraph "Mutation" createUser["Mutation: createUser(input: CreateUserInput!) → User"] end UserID -.-> ID UserName -.-> String UserRole -.-> Role InputName -.-> String InputRole -.-> Role createUser --> User createUser --> CreateUserInput
🔍 解釋這段結構:
User
是你要查的資料物件(Object Type)。User
的每個欄位(像是name
)都需要有一個型別 ➜ 這些型別大多是 Scalar Type(如String
)。role
欄位不想讓人亂填 ➜ 所以你使用 Enum Type 來限制只允許ADMIN
/USER
/GUEST
。- 當你要「新增一個使用者」時,要送一組參數 ➜ 這時你定義
CreateUserInput
(Input Type)。 CreateUserInput
的欄位結構,也一樣會用到 Scalar 和 Enum Type。
實際對照範例
enum Role {
ADMIN
USER
GUEST
}
input CreateUserInput {
name: String!
role: Role!
}
type User {
id: ID!
name: String!
role: Role!
}
type Mutation {
createUser(input: CreateUserInput!): User
}
這段 Schema 裡:
User
是一個「結果型別」,通常來自資料庫CreateUserInput
是一個「參數型別」,用來傳入前端資料String
、ID
是基本型別(Scalar)Role
是列舉型別(Enum),可用於User
和CreateUserInput
總結:它們如何合作?
你可以這樣記:
🔁 Scalar + Enum 是基本「材料」
📦 Input Type 是前端提供的「料理包」
🧾 Object Type 是後端回給前端的「餐點結果」
在一次 GraphQL 操作中,它們會這樣分工:
階段 | 使用的 Type | 說明 |
---|---|---|
前端送參數 | Input Type + Scalar/Enum | 前端整理好資料、送給後端 |
後端驗證參數 | Input Type | 確保資料格式正確 |
後端查詢資料庫 | – | 不一定直接使用 Type,但會參考欄位結構 |
後端回傳資料 | Object Type + Scalar/Enum | 把查到的資料回傳給前端 |
從 Schema 到實際查詢的流程:一筆資料如何從資料庫到畫面?
GraphQL 的強大之處,不只是能自由查資料,而是它建立了一套結構清楚、流程明確的查詢機制。
這個流程從 Schema 開始,貫穿前端、後端、資料庫,直到資料回到使用者手上。
以下是一個簡單又完整的查詢流程範例,帶你理解每一步背後的運作邏輯:
後端定義 Schema(建立介面契約)
首先,後端要定義一份 Schema,讓前端知道「你可以查什麼資料、怎麼查」。
範例:
type Query {
getUser(id: ID!): User
}
type User {
id: ID!
name: String!
email: String!
}
這段 Schema 表示:
- 你可以查詢一個叫做
getUser
的 Query - 它需要傳入一個參數
id
(類型為 ID 且必填) - 回傳的結果會是
User
這個資料型別,包含三個欄位
✅ 這就像是一張 API 菜單,列出前端可以點的服務與回應內容。
前端根據 Schema 發送請求(撰寫查詢指令)
一旦 Schema 被定義好,前端就可以直接根據它寫查詢語法。
例如,要查 ID 為 "123"
的使用者資料,可以寫這樣的 GraphQL 查詢:
query {
getUser(id: "123") {
id
name
email
}
}
這裡前端表達的是:
- 我要使用
getUser
這個 API - 傳入參數
id: "123"
- 並且只需要回傳
id
、name
、email
這三個欄位資料(不需要其他欄位)
✅ 這是 GraphQL 查詢最大的特色:前端精確決定要什麼欄位,不多不少。
後端驗證請求是否合法(Schema 驗證)
GraphQL Server 收到請求後,不會立刻查資料,而是會先根據 Schema 驗證這個請求的正確性:
getUser
是否存在於定義好的 Query 裡?id
是否有被傳進來?id
的型別是否為ID
?- 要求的欄位(
id
、name
、email
)是否真的在User
裡面有定義?
這些檢查全部通過之後,才會執行查詢邏輯。
✅ 這步驟就是 GraphQL 天生的「防呆」機制,幫助你在第一關就擋掉錯誤。
後端執行 Resolver,查詢資料來源(通常是資料庫)
經過驗證後,後端會執行對應的 resolver function,把資料撈出來。
範例(JavaScript):
const resolvers = {
Query: {
getUser: async (_, { id }) => {
return db.user.findById(id); // 使用 ORM 或 SQL 查資料庫
}
}
};
這裡的邏輯可能會:
- 呼叫資料庫的
users
資料表 - 撈出 id 為
"123"
的那一筆資料 - 將資料組成一個符合
User
型別格式的物件
✅ Resolver 就是 GraphQL 的「資料搬運工」,幫你把對的資料搬回來。
GraphQL Server 根據欄位需求,組裝回傳結果
GraphQL Server 拿到資料之後,會依照查詢指令中所列的欄位(field)進行資料挑選:
前端只要求了這三個欄位:
{
id
name
email
}
後端就只會回傳這三個欄位的資料,而不會多給像是密碼、建立時間、角色之類的資料。
範例回傳 JSON:
{
"data": {
"getUser": {
"id": "123",
"name": "小明",
"email": "[email protected]"
}
}
}
✅ GraphQL 最大的特點就是:回傳資料剛剛好,只給你需要的內容。
小總結:從 Schema 到畫面,完整流程如下:
flowchart LR A[Schema 定義] --> B[前端查詢] B --> C[請求驗證] C --> D[Resolver 查資料] D --> E[回傳結果] style A fill:#f9d5e5,stroke:#333,stroke-width:2px style B fill:#eeeeee,stroke:#333,stroke-width:2px style C fill:#e3f0f9,stroke:#333,stroke-width:2px style D fill:#d5f5e3,stroke:#333,stroke-width:2px style E fill:#fdebd0,stroke:#333,stroke-width:2px
對照流程圖:
- 後端 Schema 設計 ← 提供操作與欄位定義
- 前端寫查詢語法 ← 根據 schema 指定需要的欄位
- Server 驗證指令合法性
- 後端 Resolver 去資料庫查資料
- Server 將結果包成 JSON 回傳給前端
✅ 這個流程是所有 GraphQL 查詢的核心運作模式。
一旦你理解這整條鏈路,使用 GraphQL 查資料就變得自然又高效!
為什麼學會 Schema 這麼重要?
GraphQL 的核心理念就是:Schema 為中心(Schema-first)。
一旦你掌握了 Schema,就等於掌握了這個系統的語言、邊界、規則與能力範圍。
以下是學會 Schema 帶來的三大核心好處,並搭配實際開發場景說明:
讓開發更快:Schema 就是最完整的 API 說明書
在傳統 REST API 的開發中,前端常常會有這些困擾:
- 「這個欄位要傳什麼?有沒有格式限制?」
- 「我打這個 API 會回傳什麼資料?會不會漏欄位?」
- 「後端完成了嗎?為什麼找不到文件?」
而 GraphQL Schema 的出現,直接取代了 API 文件的角色。
因為它會明確定義每一個查詢、每一筆資料、每個欄位的格式與類型。
✅ 實際開發優勢:
- 前端只要開啟一個 GraphQL playground(例如 Apollo Studio、GraphiQL),就可以即時查到可以查什麼、傳什麼、會回什麼。
- 開發過程中不用等後端寫文件,也不需要來回問「這個 API 是什麼意思?」
- 還能搭配 IntelliSense,自動補齊指令與欄位名稱,大幅減少錯打錯拼。
🟢 學會 Schema = 能自己探索 API,快速開發,不靠人解釋。
讓資料更安全:Schema 是資料的驗證機制
在 GraphQL 中,每一個欄位都必須在 Schema 裡明確指定型別與規則。
例如:
type Post {
id: ID!
title: String!
content: String
}
這代表:
id
和title
是必填欄位(不能為 null)content
是可選欄位(可以為 null)- 三個欄位的資料型別都受到嚴格限制(不能亂傳數字給 String)
這種 Schema 驅動的驗證機制,會在前端請求送出之前或後端解析資料之前就攔下錯誤,避免:
- 傳錯格式的資料(例如把布林值傳成字串)
- 漏傳必填欄位(例如漏了
title
) - 企圖存取不該出現的欄位(例如亂猜一個
authorEmail
)
✅ 實際開發優勢:
- 不需要額外寫很多「驗證邏輯」就能確保資料正確
- 減少錯誤請求進入後端系統,提升穩定性與可預測性
- 如果你是後端開發者,也不用怕資料被寫壞或結構亂掉
🛡️ 學會 Schema = 系統自帶防呆機制,資料安全又乾淨。
讓團隊更有共識:Schema 是前後端的共同合約
GraphQL 的設計理念是:「一份 Schema,整個團隊通用」。
這份 Schema 不只是程式碼,還是整個團隊的協作規範與共識文件:
- 後端根據 Schema 開發 Resolver,提供資料來源
- 前端根據 Schema 撰寫查詢,選擇自己要的欄位
- 文件自動生成、型別自動對齊,雙方都清楚「資料長什麼樣子」
比起 REST API 常見的「前端亂猜後端會給什麼、後端不知道前端會怎麼用」。
GraphQL 的 Schema 為整個團隊建立了明確且同步的介面契約。
✅ 實際團隊合作優勢:
- 每個人都可以打開同一份 Schema,馬上理解整體資料模型與使用方式
- 新人加入團隊,只要讀 Schema 就能上手
- 即使後端還沒實作資料源,前端也可以用 mock schema 模擬開發流程(Mock Server)
🤝 學會 Schema = 擁有一份團隊溝通的「單一真相來源」(single source of truth)
總結:掌握 Schema,就是掌握 GraphQL 的核心力量
不論你是前端、後端、全端,甚至產品經理,只要你要和資料打交道,GraphQL Schema 就是你最該理解、最該信任的一份地圖。
- 想開發快 ➜ 看 Schema 就知道怎麼查資料
- 想寫資料準 ➜ Schema 幫你防呆
- 想合作順 ➜ Schema 是大家共通語言
📌 Schema-first 的思維,是你進入現代前後端協作世界的入場券。