GraphQL 入門與 REST 比較

是什麼?

GraphQL 是 Facebook 在 2015 年開源的查詢語言執行引擎。它用一個端點(通常是 /graphql)處理所有請求,由客戶端在查詢中指定需要的資料結構。

ℹ️GraphQL 不是資料庫

GraphQL 是 API 層的查詢語言,不是資料庫查詢語言。它位於前端和後端之間,後端仍然用 SQL / ORM 操作資料庫。

核心觀念

常見誤區

⚠️常見誤區

  • GraphQL 一定比 REST 好:GraphQL 增加了伺服器端複雜度(Schema 維護、N+1 查詢問題、快取困難),簡單 CRUD 用 REST 更合適
  • GraphQL 自動解決效能問題:不處理 N+1 問題的 GraphQL 比 REST 更慢。必須搭配 DataLoader 批次查詢
  • 一個前端頁面只發一次 GraphQL 請求:過度合併查詢反而降低可快取性,合理拆分查詢更好

流程/步驟

1

定義 Schema

用 SDL 宣告 Type、Query、Mutation

2

實作 Resolver

每個欄位寫對應的資料取得邏輯

3

客戶端組查詢

前端用 GraphQL 語法指定需要的欄位

4

引擎解析執行

GraphQL 引擎解析查詢,逐層呼叫 Resolver

5

回傳精確資料

只回傳客戶端指定的欄位,不多不少

流程解讀:GraphQL 從 Schema 定義開始,它是前後端的合約。Resolver 是資料的實際來源。客戶端組合查詢語句,引擎解析後逐層呼叫 Resolver 取得資料,最終只回傳查詢中指定的欄位。

程式碼範例

C# 版本

csharp
// Hot Chocolate — .NET GraphQL Server
// Schema 定義
public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Email { get; set; } = "";
    public List<Order> Orders { get; set; } = new();
}
 
public class Query
{
    public User GetUser(int id, [Service] IUserService service)
        => service.GetById(id);
 
    public IQueryable<User> GetUsers([Service] AppDbContext db)
        => db.Users;
}
 
public class Mutation
{
    public User CreateUser(string name, string email, [Service] IUserService service)
        => service.Create(name, email);
}
 
// Program.cs
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddMutationType<Mutation>()
    .AddFiltering()
    .AddSorting();

TypeScript 版本

typescript
// Apollo Server — GraphQL
import { ApolloServer } from "@apollo/server";
 
const typeDefs = `#graphql
  type User {
    id: ID!
    name: String!
    email: String!
    orders: [Order!]!
  }
 
  type Order {
    id: ID!
    total: Float!
  }
 
  type Query {
    user(id: ID!): User
    users: [User!]!
  }
 
  type Mutation {
    createUser(name: String!, email: String!): User!
  }
`;
 
const resolvers = {
  Query: {
    user: (_: any, { id }: { id: string }) => userService.getById(id),
    users: () => userService.getAll(),
  },
  User: {
    orders: (parent: User) => orderService.getByUserId(parent.id),
  },
  Mutation: {
    createUser: (_: any, { name, email }: { name: string; email: string }) =>
      userService.create(name, email),
  },
};
 
// 客戶端查詢 — 精確指定需要的欄位
const GET_USER = `
  query GetUser($id: ID!) {
    user(id: $id) {
      name
      email
      orders {
        total
      }
    }
  }
`;

Python 版本

python
# Strawberry — Python GraphQL
import strawberry
from typing import List
 
@strawberry.type
class User:
    id: int
    name: str
    email: str
 
    @strawberry.field
    async def orders(self) -> List["Order"]:
        return await order_service.get_by_user_id(self.id)
 
@strawberry.type
class Order:
    id: int
    total: float
 
@strawberry.type
class Query:
    @strawberry.field
    async def user(self, id: int) -> User | None:
        return await user_service.get_by_id(id)
 
@strawberry.type
class Mutation:
    @strawberry.mutation
    async def create_user(self, name: str, email: str) -> User:
        return await user_service.create(name, email)
 
schema = strawberry.Schema(query=Query, mutation=Mutation)

架構圖/概念圖

Client (Query)
sends query
GraphQL Engine
validates against
Schema (SDL)
Resolvers
fetches from
Data Sources

客戶端發送查詢到 GraphQL Engine,Engine 先用 Schema 驗證查詢語法,然後逐層呼叫 Resolver 從各種 Data Source 取得資料。最終只回傳查詢中指定的欄位。

實戰補充

Q: REST 和 GraphQL 怎麼選?

A: 用 REST:簡單 CRUD、公開 API、強快取需求、團隊不熟 GraphQL。用 GraphQL:前端需要彈性查詢、多種客戶端(Web/Mobile)需要不同欄位、資料關聯複雜需要一次取得多層資料。

Q: GraphQL 的 N+1 問題是什麼?

A: 查詢 10 個 User 時,每個 User 的 orders 欄位各觸發一次 DB 查詢,總共 1 + 10 = 11 次查詢。解法:用 DataLoader 批次收集所有 userId,一次查詢所有 orders。

Q: GraphQL 怎麼做快取?

A: REST 靠 URL 做 HTTP 快取很簡單,GraphQL 因為只有一個端點所以 HTTP 快取失效。改用 Persisted Queries(把查詢存成 hash)或 Apollo Client 的 normalized cache

理解測驗

🤔 GraphQL 的 Over-fetching 問題指的是什麼?

🤔 GraphQL 的 Resolver 負責什麼?

🤔 什麼是 GraphQL 的 N+1 問題的標準解法?

重點整理

💡一句話記住

GraphQL = 前端點菜、後端精準出餐。 口訣:「一個端點打天下,要啥給啥不浪費」

| 對比 | REST | GraphQL | |------|------|---------| | 端點 | 多個 URL | 單一 /graphql | | 資料精確度 | 固定回傳結構 | 客戶端指定欄位 | | 快取 | HTTP 快取簡單 | 需要額外方案 | | 學習成本 | 低 | 中高 | | 適合場景 | 簡單 CRUD、公開 API | 複雜關聯、多端需求 |

你可能也想看

API 認證機制API 限流設計

按 ← → 鍵切換課程