GraphQL 變數用法完整指南:從入門到實作

更新日期: 2025 年 6 月 10 日

在學習 GraphQL 的初期,我們常常會直接把參數值寫在查詢語法中,例如:

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

這樣雖然可以正確執行,但隨著專案變大,這種寫法會出現以下問題:

  • ❌ 查詢語法與參數綁死,無法重複利用
  • ❌ 每次參數不同就得改語法,不利測試與快取
  • ❌ 不利於與前端框架整合,像 React、Vue

GraphQL 變數系統就是為了解決這些問題而設計的!

它讓你可以將查詢邏輯與參數拆開,讓查詢語法更彈性、更乾淨、也更容易管理。

GraphQL 查詢語法的三個組成部分

當我們在 GraphQL 中使用變數時,整體的查詢邏輯會被分成三個主要部分,這三個部分各司其職,搭配起來才能正確執行查詢:

Operation 定義區塊(Operation Definition)

這個區塊是 GraphQL 查詢的開頭,用來定義這次的操作類型(如 query 或 mutation)、操作名稱(Operation Name),以及你要用到的變數。

query GetUser($id: ID!)

這行的意思是:

  • query:代表這是一個讀取資料的查詢(還有 mutationsubscription
  • GetUser:這是這段查詢的名稱(Operation Name)
  • ($id: ID!):宣告了一個名為 $id 的變數,它的型別是 ID,且是「必填」的(因為有 !

重點:變數一定要在 Operation 區塊「先宣告」,才能在後面使用!

在 GraphQL 中,變數是一種外部傳入的資料,不像 JavaScript 可以直接在程式中宣告變數再使用。

GraphQL 查詢是一種嚴謹的語言,它必須在「查詢開頭的 Operation 區塊」明確宣告你要使用哪些變數、每個變數的名稱是什麼、型別是什麼、是否必填,這樣執行引擎才會知道如何處理。

🔧 如果你沒有先宣告會發生什麼事?

假設你寫了這段查詢:

query {
  getUser(id: $id) {
    name
  }
}

但沒有宣告 $id,系統會回傳類似以下錯誤:

"Variable \"$id\" is not defined."

也就是說,GraphQL 無法處理一個沒被宣告的變數,因為它不知道 $id 是什麼型別、是否必填、該怎麼驗證。

📘 正確的做法:在 Operation 定義中宣告變數

query GetUser($id: ID!) {
  getUser(id: $id) {
    name
  }
}

在這裡,我們宣告了:

  • $id 是變數名稱
  • ID 是變數的型別(通常是識別碼)
  • ! 表示這個變數是必填的,不能傳 null 或不傳

一旦這樣宣告,後面的查詢主體就能安全地使用 $id,而 GraphQL 執行引擎也能幫你自動做類型檢查與驗證。

❓補充:要使用變數就一定要定義 Operation Name 嗎?

不是一定要!但有強烈建議。

✅ 不一定需要 Operation Name

以下這樣也是合法語法,可以執行:

query ($id: ID!) {
  getUser(id: $id) {
    name
    email
  }
}

這樣的寫法「省略了 Operation Name」,仍可正常使用變數,但查詢沒有名字。

⚠️ 為什麼建議加上 Operation Name?

加上 Operation Name 有以下幾個好處:

  • 📦 方便除錯與追蹤:伺服器錯誤時會告訴你是哪個 operation 出錯
  • 🔁 支援多個查詢或 Mutation 並存時,可以指定要執行哪一個
  • 📋 提高可讀性與維護性,讓查詢的意圖更清楚

因此在實務開發中,尤其是多人協作或大型專案,幾乎都會加上 Operation Name

查詢主體區塊(Query Body):查詢的核心執行內容

查詢主體區塊是 GraphQL 語法中最關鍵的部分,這裡定義了你到底要從伺服器拿哪些資料、要哪些欄位,以及查詢時要用什麼參數來篩選

這部分是 GraphQL 的靈魂,也是與 REST 最大不同的地方 —— 你可以精確選擇資料欄位,而不是整包回傳

✅ 查詢主體的範例

{
  getUser(id: $id) {
    name
    email
  }
}

這段語法代表:

  • 執行 getUser 查詢(這是一個由後端 Schema 定義的查詢)
  • 透過參數 id,傳入一個變數 $id(這個 $id 是在 Operation 區塊定義好的變數)
  • 回傳的資料只包含 nameemail 這兩個欄位(可以自由決定欄位,不必拿整包)

為什麼這裡要用變數?

你也可以直接這樣寫:

{
  getUser(id: "123") {
    name
    email
  }
}

這樣雖然查得出來資料,但:

寫法問題點
id: "123"寫死在查詢中,無法動態修改資料
$id可以從外部傳入,讓查詢具有彈性與重複性

所以使用 $id 變數可以將查詢語法模板化、標準化、易於重用與測試

變數傳入區塊(Variables Payload):執行查詢時注入參數

這一區塊雖然不寫在 GraphQL 語法裡,但卻是讓變數生效的關鍵橋梁

它是在實際執行查詢時,傳入變數對應的值。

✅ 傳入變數的格式(JSON)

{
  "id": "123"
}

這代表:

  • 在查詢語法中宣告了 $id,這裡把它實際設定為字串 "123"
  • GraphQL 執行引擎會在處理查詢時,自動把 $id 替換成 "123"

🛠 常見使用情境

工具/框架傳入方式
GraphQL Playground下方的 Query Variables 區塊
Apollo Client透過 variables 屬性傳入
Postman在 body 的 variables 欄位傳入
fetch/Axios以 JSON 傳入 variables 欄位

常見資料型別與寫法

在使用 GraphQL 撰寫查詢與 mutation 時,變數是不可或缺的一環。

而這些變數都需要明確指定型別(type),這是 GraphQL 的一大特性:型別強制且嚴謹

為什麼 GraphQL 要指定型別?

  • ✅ 強型別幫助你在查詢前就發現錯誤(例如傳錯型別)
  • ✅ API 自動產生文件(Schema)更清楚
  • ✅ 編輯器有 IntelliSense,自動補全更方便
  • ✅ 能與 TypeScript 等靜態型別系統無縫整合

常見內建型別與用法

型別說明與使用情境範例變數傳入值
String一般文字、使用者名稱、電子信箱、備註內容等"[email protected]"
Int整數數字,例如:年齡、頁數、數量等28
Float浮點數,例如:價格、小數比例、評分等3.14
Boolean布林值,用於開關、是否啟用、是否為管理員等二元邏輯判斷true 或 false
ID唯一識別碼,通常用於資料庫主鍵、使用者 ID、文章 ID 等(實際上是 String)"user_8237"
[String]字串陣列,例如搜尋多個關鍵字、標籤清單、輸入多個 Email 等["foo", "bar", "baz"]

複合型別:自定義輸入物件(Input Type)

除了上面內建型別外,GraphQL 也支援自定義輸入物件(Custom Input Object),常用於 mutation 中一次傳入多個欄位。

✅ 宣告方式(後端)

input CreateUserInput {
  name: String!
  email: String!
  age: Int
}

✅ 使用方式(前端)

查詢語法:

mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
  }
}

變數傳入值:

{
  "input": {
    "name": "小明",
    "email": "[email protected]",
    "age": 18
  }
}

這種方式非常適合表單資料送出,讓 mutation 更簡潔、易維護。

❗什麼是 !?為什麼要加在型別後面?

在 GraphQL 中,每個變數型別都可以加上或不加 !,這會決定該變數是否為必填欄位

寫法意義可否傳 null 或不傳
String!必填欄位,一定要傳值且不可為 null❌ 不可省略或為 null
String選填欄位,可以傳 null 或不傳入✅ 可以省略或傳 null

🔍 延伸補充:[String!]! 是什麼意思?

  • 外層的 !:陣列本身不能是 null
  • 內層的 !:陣列中的每一個元素都不能是 null

✅ 這樣可以確保傳進來的是一個「完整且沒有空值」的清單

前端如何傳遞變數?(以 React + Apollo Client 為例)

在前端專案中,我們通常不會手動組裝 GraphQL 字串和變數,因為這樣容易出錯又難維護。

這時候可以使用 Apollo Client 這類專門的 GraphQL 前端工具,它幫我們:

  • 📦 自動管理變數注入
  • 🚀 發送請求到後端 GraphQL Server
  • ✅ 處理回傳資料與錯誤

讓我們先看一段基本範例:

const GET_USER = gql`
  query GetUser($id: ID!) {
    getUser(id: $id) {
      name
      email
    }
  }
`;

const { data, loading, error } = useQuery(GET_USER, {
  variables: { id: "123" }
});

這段程式碼可分為兩大部分:

gql 標記的 GraphQL 查詢語法

const GET_USER = gql`
  query GetUser($id: ID!) {
    getUser(id: $id) {
      name
      email
    }
  }
`;

這裡用 gql 函式標記了一段查詢語法,它是 Apollo 提供的函數,用來讓 JavaScript 理解這段字串是 GraphQL 查詢。

  • GetUser 是查詢的名稱(方便除錯與追蹤)
  • $id 是查詢中要使用的變數
  • getUser(id: $id) 是實際執行查詢並插入變數的地方

📌 這段查詢語法就像是一個「待執行的模板」,變數還沒真正填進去。

使用 useQuery() 傳入變數並執行查詢

useQuery(GET_USER, {
  variables: { id: "123" }
});

這裡是查詢的實際執行點:

  • GET_USER:剛剛定義好的查詢語法模板
  • variables:是一個物件,裡面定義了每個變數名稱與對應值
  • 在這裡就是傳入 id: "123",讓 $id 變數有值可以使用

可視化理解:就像呼叫一個函數

你可以把整個過程想像成下面這樣:

function getUserData(id) {
  return getUser(id);
}

// 呼叫函數時傳入參數
getUserData("123");

在 GraphQL 的世界中:

  • 查詢語法是函數定義(有變數)
  • useQuery() 是呼叫函數(傳入變數)

延伸:變數可以是多個欄位嗎?

const GET_USERS = gql`
  query GetUsers($ids: [ID!]!) {
    users(ids: $ids) {
      id
      name
    }
  }
`;

useQuery(GET_USERS, {
  variables: { ids: ["123", "456", "789"] }
});

只要你在查詢語法裡有宣告變數(無論是單一值還是陣列、自訂輸入物件),都可以用 variables 一次性傳入。

結語:養成「變數優先」的查詢習慣

在初學 GraphQL 時,直接寫死參數是正常的起步方式,但隨著功能變多、畫面變複雜,學會使用變數能大幅提升你的查詢彈性與維護性

現在就試著把你手上的查詢改寫成使用變數的方式,體驗乾淨、簡潔又強大的 GraphQL 查詢流程吧!

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *