跳至正文

GraphQL

GraphQL 是一种 API 查询语言——后端定义数据结构(Schema),前端按需查询所需字段。所有请求走单一端点(通常 POST /graphql),由客户端在查询语句中精确指定要哪些字段。

Note

特点

  • 单一端点(POST /graphql)处理所有请求
  • 客户端精确指定所需字段,避免 Over-fetching(返回多余字段)和 Under-fetching(一个页面调多个接口)
  • 强类型 Schema 定义数据结构,自带 API 文档
  • 内置 Subscription 支持实时数据推送(底层可基于 WebSocket)

Note

局限

Note

适用场景

  • 多客户端(Web / Mobile)共用一套 API(GitHub API v4)
  • 移动端省流量
  • 数据关系复杂的产品需要灵活组合(Shopify、Yelp)
  • 前后端团队分离各自迭代

REST vs GraphQL 的数据形态

┏━━━━━━━━━━ REST API ━━━━━━━━━━━┓
┃                               ┃
┃  GET /api/user/1              ┃  → { id, name, email, avatar, bio, ... }
┃  GET /api/user/1/posts        ┃  → [{ id, title, content, ... }, ...]
┃  GET /api/user/1/followers    ┃  → [{ id, name, ... }, ...]
┃                               ┃
┃  3 个请求,每个可能返回多余字段 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛


┏━━━━━━━━━ GraphQL API ━━━━━━━━━┓
┃                               ┃
┃  POST /graphql                ┃    {
┃  query {                      ┃      user: {
┃    user(id: 1) {              ┃        name: "Alice",
┃      name                     ┃  →     posts: [{ title: "..." }],
┃      followers { name }       ┃        followers: [{ name: "..." }]
┃    }                          ┃      }
┃  }                            ┃    }
┃                               ┃
┃  1 个请求,只返回所需字段      ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

基础概念

概念谁负责一句话说明详细
Schema后端定义有哪些数据类型和操作接口(API 的契约)详见
Resolver后端每个字段如何获取数据的函数详见
Query前端按需查询数据(类似 GET)详见
Mutation前端按需修改数据(类似 POST/PUT/DELETE)详见
Subscription前端订阅实时数据推送(底层常用 WebSocket)详见
codegen工具读取后端 Schema,为前端生成 TypeScript 类型详见

Note

要点:Schema 和 Query 的关系

Schema 和 Query 语法看起来很像(都用花括号描述结构),但本质完全不同:

  • Schema(后端写)= 类型定义,声明”有什么数据、什么类型”,类似数据库建表 CREATE TABLE
  • Query(前端写)= 查询语句,从 Schema 定义的字段中”挑这次要哪些”,类似 SELECT name, title FROM ...

Schema 整个 API 只有一份(后端维护),Query 有很多份(每个页面/组件按需写不同的查询)。

基本流程

①  后端定义 Schema(SDL)

②  后端实现 Resolver(每个字段如何取数据)

③  codegen 读取 Schema → 生成前端 TS 类型

④  前端写 Query / Mutation → 发到 /graphql 端点

⑤  服务端执行对应 Resolver → 返回 JSON

详细步骤:

  1. 后端定义 Schema —— 用 SDL 声明数据类型和操作接口(详见
  2. 后端实现 Resolver —— 为 Schema 中的每个字段编写数据获取逻辑(详见
  3. codegen 生成类型 —— 读取后端 Schema,为前端生成 TypeScript 类型(详见
  4. 前端写查询语句 —— 用 Query/Mutation 指定需要的字段(详见

多服务合并:Federation 与 Schema Stitching

企业级 GraphQL 常有多个后端服务,需要把各自的 Schema 合并成一张大图:

方案说明
Apollo Federation(事实标准)每个子服务(subgraph)声明自己负责的类型和字段,Gateway 用 @key / @extends 等指令组合;支持跨服务的关联查询
Schema Stitching老方案,Gateway 运行时合并多个远端 Schema;灵活但重构频繁、Apollo 已降为替代选项
GraphQL Mesh更通用的”聚合层”,能把 REST / gRPC / OpenAPI / 数据库当 GraphQL 源

Note

选型

  • 多团队多后端、期望跨服务关联查询 → Apollo Federation
  • 已有异构服务(REST / gRPC)想统一出口 → GraphQL Mesh
  • 单 GraphQL 服务 → 不需要 Federation,直接单 Schema 即可

争议与取舍

GraphQL 的核心优势是客户端灵活查询。但这个灵活性不是免费的,它带来了一系列成本:

成本具体问题
N+1 问题嵌套查询天然触发逐条查询,必须引入 DataLoader
缓存所有请求 POST /graphql,HTTP 缓存和 CDN 完全失效,必须用客户端缓存
安全客户端可构造任意查询,必须额外实现深度 / 复杂度限制
监控HTTP 状态码始终 200,传统监控体系需要额外适配
服务端复杂度维护 Schema + Resolver + DataLoader + 权限控制,每新增字段都要写 Resolver
开发流程后端写 Schema → Resolver → codegen → 前端写 Query → codegen 再生成 Hook,改一个字段要动多处

Note

选型判断

当场景不需要”客户端灵活查询”这个能力时,上面这些成本就变成了纯开销。

  • 适合 GraphQL:多客户端共用 API、数据关系复杂需要灵活组合、前后端分离各自迭代
  • 不适合:TS 全栈项目用 tRPC 成本更低,简单 CRUD / 公开 API 用 REST 更成熟,微服务间通信用 gRPC 更合适

GraphQL vs tRPC

都解决了 REST 的类型安全问题,但思路完全不同:

维度GraphQLtRPC
语言和受众任意语言,可对外暴露给第三方仅 TypeScript,前后端同语言同团队
查询灵活度前端自由选字段,不同客户端按需查询服务端决定返回什么,前端只能调用
开发成本Schema → Resolver → codegen → Query,改一个字段动 4 处写个函数就是 API,零 codegen
错误和缓存始终 200 + errors,需 Apollo Cache标准 HTTP 状态码 + TRPCError,TanStack Query 管缓存
实时通信内置 Subscription支持 subscription procedure,生态不如 GraphQL 成熟
生态成熟(Apollo/Relay/urql)较小,围绕 Next.js / TanStack Query
适合场景对外 API、多客户端、多语言TS 全栈内部项目、同一团队

Note

怎么判断

  1. 先看语言限制:有非 TS 客户端或第三方要消费 API → tRPC 直接排除,选 GraphQL
  2. 再看是否需要灵活查询:多客户端需要不同字段组合 → GraphQL;单客户端固定结构 → tRPC 开发成本低得多

GraphQL vs REST

维度GraphQLREST
数据获取客户端精确指定字段,一次请求拿关联数据服务端决定返回结构,可能多余字段或多次请求
N+1 问题嵌套查询天然触发,必须引入 DataLoaderController 层一次性 JOIN,不存在
缓存HTTP 缓存失效,需客户端缓存HTTP 原生缓存 + CDN
安全客户端可构造任意查询,需深度/复杂度限制端点固定,服务端完全控制查询
错误处理始终 200 + errors 字段,支持部分成功HTTP 状态码(404/500 等)
API 演进新增字段不影响旧客户端,不需要版本号需版本控制(v1/v2)或 Header
文件上传JSON 不支持二进制,需额外方案原生支持 multipart
适合场景多客户端灵活查询、数据关系复杂、BFF 服务多端简单 CRUD、公开 API、需要 HTTP 缓存/CDN

Note

怎么判断

  1. 先看客户端数量:多客户端需要不同字段组合 → GraphQL 的灵活查询有价值
  2. 再看基础设施需求:需要 HTTP 缓存/CDN/标准监控 → REST 全链路成熟,GraphQL 每项都要额外方案