探索 .graphql 檔案:前端與後端如何分工協作?
更新日期: 2025 年 5 月 8 日
當你第一次接觸 GraphQL,可能會感到困惑:
「我在專案中看到很多
.graphql
檔案,有些在後端、有些在前端,到底誰該寫什麼?這些檔案是在做什麼用?」
事實上,.graphql
是一種純文字格式的檔案,用來撰寫 GraphQL 的語法內容,但前後端使用 .graphql
的目的與內容是完全不同的。
本篇文章將帶你從 .graphql
的角度,釐清以下三件事:
.graphql
到底可以寫哪些東西?- 前端與後端各自用
.graphql
做什麼? - 如何從這個分工建立完整的 GraphQL 開發流程?
.graphql
是什麼?它能包含哪些內容?
在 GraphQL 專案中,你經常會看到副檔名為 .graphql
的檔案。
這些檔案表面上看起來都是純文字,但其實扮演著兩種截然不同的角色,端看你是從後端還是前端的角度來使用。
.graphql
的本質是什麼?
.graphql
是一種語言檔案格式,內容是用 GraphQL 專屬的語法(GraphQL SDL:Schema Definition Language)所撰寫的。
這些檔案的用途依照場景可分為兩大類:
類型一:Schema Definitions(Schema 定義語法)→ 後端使用
這類 .graphql
檔案的目的,是定義整個 API 的資料結構與行為,屬於 GraphQL 的「型別系統」。
你可以想像它是 API 的「說明書」,負責定義:
- 有哪些資料類型(如 User、Post、Product)
- 每個類型有哪些欄位、屬性是什麼資料型別
- 哪些欄位可以為 null,哪些是必填
- 查詢與操作的進入點(Query / Mutation / Subscription)
🔧 範例:
# schema.graphql
type User {
id: ID!
name: String!
email: String
}
type Query {
user(id: ID!): User
}
type Mutation {
updateUser(id: ID!, name: String!): User
}
這些定義通常由後端工程師撰寫,會被 GraphQL Server(如 Apollo Server 或 Hasura)讀取並轉成實際可執行的 API。
這也是為什麼我們說這是後端的 GraphQL 檔案。
類型二:Operation Definitions(操作語法)→ 前端使用
另一種 .graphql
檔案則是由前端工程師撰寫,它不定義資料結構,而是定義前端要向後端請求什麼資料。
這類語法被稱為「操作語法」,包含三種主要類型:
操作類型 | 說明 |
---|---|
query | 查詢資料(最常見) |
mutation | 修改資料(新增、更新、刪除) |
subscription | 即時監聽資料變化(如聊天室、通知等場景) |
範例:
# getUser.graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
mutation UpdateUser($id: ID!, $name: String!) {
updateUser(id: $id, name: $name) {
id
name
}
}
這些查詢語法會搭配像 Apollo Client 或 Relay 的 GraphQL 客戶端工具來發送請求。
後端的 .graphql
:定義 API 規格的藍圖
在後端,.graphql
檔案的主要任務是定義 GraphQL Schema,這是整個 API 系統的根本。
也就是「你能查什麼資料」、「可以對哪些資料做哪些操作」,全部都寫在這裡。
你可以把它想像成一份 API 的契約文件(Contract),由後端定義、前端遵守。
這份契約不僅描述了資料結構,也定義了資料的操作方式,並且對整個系統提供強型別的保障。
型別與操作入口的定義
在 .graphql
Schema 檔中,最常見的語法包括:
✅ 定義資料模型(Types)
type User {
id: ID!
name: String!
email: String
}
這代表你的系統中有一種「使用者」資料,每個使用者有三個欄位:id、name 和 email,其中 id 與 name 是必填。
✅ 定義查詢進入點(Query)
type Query {
user(id: ID!): User
users: [User!]!
}
這代表前端可以透過 user(id: ...)
或 users
這些查詢操作,來取得資料。每個欄位都像是 API 的一個 endpoint。
✅ 定義變更操作(Mutation)
type Mutation {
updateUser(id: ID!, name: String!): User
deleteUser(id: ID!): Boolean
}
這些定義表示前端可以呼叫 updateUser
或 deleteUser
來改變資料,類似 REST 中的 POST / PUT / DELETE 行為。
✅ 定義即時通訊(Subscription)
type Subscription {
userAdded: User
}
如果你的應用需要即時功能(如聊天室、通知),可以使用 subscription
提供 WebSocket 資料流。
Schema 如何運作?
Schema 只是「宣告」,實際的資料來源與邏輯處理,會交給後端對應的 Resolver 函式 處理。例如:
const resolvers = {
Query: {
user: (_, { id }) => db.user.findById(id),
},
Mutation: {
updateUser: (_, { id, name }) => db.user.update(id, name),
},
}
這些 Resolver 就像是後端 API 的實作細節,Schema 則是對外公布的規則與使用說明。
前端的 .graphql
:撰寫實際操作 API 的請求語法
前端開發者也會撰寫 .graphql
檔案,但用途與後端完全不同。
前端寫的 .graphql
檔案不是用來定義 API,而是用來描述「我想向 API 請求什麼資料」。
這些操作定義會被 GraphQL Client(如 Apollo Client)解析,發送 HTTP 或 WebSocket 請求給後端的 GraphQL Server。
查詢資料:query
最基本的 GraphQL 查詢請求,功能類似 REST 的 GET:
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
修改資料:mutation
用來新增、修改或刪除資料,功能類似 POST / PUT / DELETE:
mutation UpdateUser($id: ID!, $name: String!) {
updateUser(id: $id, name: $name) {
id
name
}
}
訂閱資料:subscription
用來建立持久的即時連線,常見於聊天室、即時通知、遊戲狀態同步:
subscription OnUserAdded {
userAdded {
id
name
}
}
與 Codegen 工具結合:讓 .graphql
檔案成為型別安全的開發利器
前端開發者撰寫的 .graphql
檔案,原本只是純文字的查詢描述,用來向後端發送 API 請求。
但這樣的查詢如果每次都要手動組裝、串接、解析回傳資料,就會:
- 重複撰寫相同的資料結構
- 無法享受 TypeScript 的型別檢查
- 增加錯誤與維護風險
這時候就可以搭配 GraphQL Code Generator(簡稱 Codegen)來解決這些問題。
Codegen 是什麼?
Codegen 是一套工具,可以根據:
- 後端提供的 GraphQL Schema
- 前端撰寫的
.graphql
查詢檔案
自動產生:
- TypeScript 型別定義
- React 專用的查詢 Hook 函式(也支援 Vue、Svelte 等架構)
- API 請求參數與回傳資料的型別推導
工作流程圖(概念說明):
flowchart TD %% 後端部分 subgraph Backend["後端 schema 定義"] SchemaFile["schema.graphql"] TypeDefs["type User {...}<br />type Query {...}"] SchemaFile --> TypeDefs TypeDefs --> TypeSystem["型別系統"] TypeDefs --> EntryPoint["操作系統 (定義入口)"] end %% API 服務 API["API 服務"] Backend --> API %% 前端部分 subgraph Frontend["前端查詢請求檔案"] QueryFile["getUser.graphql"] QueryOp["query GetUser { ... }"] QueryFile --> QueryOp QueryOp --> FrontOp["前端發送的操作"] end API --> Frontend %% Codegen 部分 Codegen["搭配 Codegen"] Frontend --> Codegen Backend --> Codegen %% 輸出 Safety["產出型別安全函式<br />(如:useGetUserQuery)"] Codegen --> Safety %% 樣式設定 classDef backend fill:#f9f9f9,stroke:#333,stroke-width:1px classDef frontend fill:#f9f9f9,stroke:#333,stroke-width:1px classDef file fill:#fffde7,stroke:#fbc02d,stroke-width:1px classDef service fill:#e1f5fe,stroke:#0288d1,stroke-width:1px classDef output fill:#e8f5e9,stroke:#2e7d32,stroke-width:1px class Backend backend class Frontend frontend class SchemaFile,TypeDefs,QueryFile,QueryOp file class API,Codegen service class Safety output
📄 .graphql
原始查詢檔案範例(前端撰寫)
# user.graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
Codegen 自動產出結果(部分範例)
// 自動產生的 hook 函式與型別
export function useGetUserQuery(
options: QueryHookOptions<GetUserQuery, GetUserQueryVariables>
) {
return useQuery<GetUserQuery, GetUserQueryVariables>(
GetUserDocument,
options
);
}
實際用法:像操作 React Query 一樣簡單
const { data, loading, error } = useGetUserQuery({
variables: { id: "123" }
});
這段程式碼背後的查詢、型別檢查與 API 串接邏輯,都由 Codegen 自動幫你產生,讓你:
- 🔐 自動享有回傳資料的完整型別提示
- 🧼 避免拼錯欄位名、錯用型別等常見錯誤
- 🧩 自動對應後端 schema 的資料格式,若 schema 改動能即時發現錯誤
🔧 實務應用:常見工具鏈整合
技術 | 整合方式說明 |
---|---|
Apollo Client | 可產出 useXxxQuery / useXxxMutation Hook |
urql | 可搭配產生 useQuery, useMutation 變數與型別 |
React | 自動產出符合 React 結構的 Hook 函式與型別 |
TypeScript | 查詢變數與回傳結果全程型別檢查 |
常見問題 FAQ:關於 .graphql
你可能會有的疑問
Q1:前端的 .graphql
檔案會送到後端執行嗎?
✅ 會的,但並不是直接「送檔案」過去。
前端的 .graphql
檔案中撰寫的是查詢語法(Query / Mutation / Subscription)。
這些查詢會在程式執行時被讀取,轉換成一段純文字的 GraphQL 查詢字串,然後由前端程式發送成 HTTP 請求(大多是 POST
)。
範例:
POST /graphql
Content-Type: application/json
{
"query": "query GetUser($id: ID!) { user(id: $id) { id name } }",
"variables": { "id": "123" }
}
後端的 GraphQL Server 會接收到這段查詢文字,根據事先定義好的 Schema 和 Resolver,解析並執行對應的邏輯後回傳結果。
Q2:所有前端查詢都一定要寫在 .graphql
檔案嗎?
🟡 不一定,但這樣做有許多好處。
你可以選擇:
- 將查詢語法直接寫在 JS/TS 程式碼中(稱為 inline query):
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
`;
- 或是獨立拆成
.graphql
檔案 並集中管理:
# user.graphql
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
📌 建議在中大型專案中,將查詢獨立為 .graphql
檔案,原因包括:
原因 | 好處 |
---|---|
結構清晰、模組化 | 每個查詢檔可依功能命名,便於查找與重用 |
可搭配 Codegen 使用 | 自動產出型別與查詢函式,提高開發效率與安全 |
更容易做 lint / 測試 | .graphql 檔案可被靜態分析工具讀取,便於執行驗證或產出文件 |
Q3:可以在同一個 .graphql
檔案中寫多個 query / mutation 嗎?
✅ 可以,只要每個操作定義都有唯一名稱即可。
範例:
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
mutation UpdateUser($id: ID!, $name: String!) {
updateUser(id: $id, name: $name) {
id
name
}
}
這樣做在小型專案或同一功能模組中很方便,但如果查詢越來越多,建議一個檔案一個操作,方便維護與追蹤。
💡 補充:Codegen 也支援多操作一檔的模式,只要名稱不衝突就能正確產出多個對應函式與型別。
結語:.graphql
是前後端共同使用的語言標準
.graphql
檔案不只是純文字,更是一種溝通協議與開發工具。
透過 .graphql
:
- 後端 定義 API 規則、資料模型與可操作的端點
- 前端 撰寫實際查詢語法,清楚描述需要什麼資料、如何操作
- 工具鏈(如 Codegen) 則讓這兩端自動對齊、型別同步,減少錯誤與溝通成本
🎯 初學者只要掌握三個核心觀念:
.graphql
檔案可以分為 Schema(後端)與 Operation(前端)兩種用途- 前端查詢寫在
.graphql
檔案中更結構化,也能搭配工具產出型別與查詢函式 - Codegen 是讓這一切發揮威力的關鍵橋樑,將查詢轉為型別安全的開發體驗
這就是為什麼說:「學好 .graphql
,是做好 GraphQL 開發的第一步。」🧩