Chapter 01

GraphQL 核心概念与 REST 对比

理解 GraphQL 解决的问题,建立核心概念认知体系

REST 的问题

过度获取(Over-fetching)

假设你在构建一个移动端用户列表页面,只需要显示用户名和头像。但 REST API 的 GET /users 返回了每个用户的所有信息:

{
  "id": "u1",
  "name": "Alice",
  "email": "alice@example.com",
  "phone": "+86-138-0000-0001",
  "address": { "city": "Beijing", "street": "..." },
  "preferences": { "theme": "dark", "language": "zh" },
  "createdAt": "2023-01-01T00:00:00Z",
  "lastLogin": "2024-01-15T10:30:00Z"
  // ...还有更多字段
}

你只用了 name 和头像 URL,却传输了十几个字段的数据。在 3G 网络下,这会显著增加加载时间。

欠获取(Under-fetching)与 N+1 问题

另一个常见问题是需要多次请求才能获取所需数据。例如显示用户列表,每个用户下面还需要显示该用户最新的3篇文章:

# 第1个请求:获取用户列表
GET /users
# 返回10个用户

# 第2-11个请求:为每个用户获取文章(N+1 问题!)
GET /users/u1/posts?limit=3
GET /users/u2/posts?limit=3
...
GET /users/u10/posts?limit=3
# 共需要 11 次 HTTP 请求

GraphQL 的诞生

Facebook 的解决方案(2012)

2012 年,Facebook 的 iOS 应用移动团队在构建 News Feed 时遇到了严重的性能问题。他们的 REST API 是为桌面 Web 设计的,无法满足移动端的精确数据需求。工程师 Lee Byron、Nick Schrock 和 Dan Schafer 开始设计一种新的查询语言——GraphQL(Graph Query Language)。

核心思想:让客户端(而不是服务器)决定需要哪些数据

2012 年
Facebook 内部开始开发 GraphQL,用于驱动 iOS App 的 News Feed。
2015 年
在 React.js Conf 上公开发布 GraphQL 规范和参考实现(graphql-js)。
2016 年
GitHub 将公开 API 从 REST v3 迁移到 GraphQL v4,成为重大标志事件。
2018 年
GraphQL Foundation 在 Linux Foundation 旗下成立,确保项目中立治理。
今日
GitHub、Shopify、Twitter/X、Airbnb、Netflix、PayPal 等都在生产环境使用 GraphQL。

用 GraphQL 解决同样问题

# 只请求需要的字段:用户名 + 最新3篇文章标题
query UserListWithPosts {
  users {
    id
    name
    avatar
    posts(limit: 3) {
      id
      title
    }
  }
}

一次请求,精确获取所需数据,没有多余字段,也没有额外的 N+1 请求。

GraphQL vs REST vs gRPC

特性 REST GraphQL gRPC
端点数量 多个端点(/users, /posts...) 单一端点(/graphql) 方法调用(protobuf)
数据获取 固定响应结构 客户端精确声明 固定 proto 定义
类型系统 需要 OpenAPI 等额外工具 内置强类型 Schema protobuf 强类型
实时支持 需要 SSE/WebSocket 扩展 内置 Subscription 内置 streaming
缓存 HTTP 缓存天然支持 需要额外工具(APQ) 应用层缓存
适用场景 简单 CRUD,公开 API 复杂数据图,移动端 内部微服务,高性能

核心概念

Schema
GraphQL API 的类型系统契约,用 SDL(Schema Definition Language)描述所有可查询的数据和操作。Schema 是前后端协作的核心文档。
Query
读取数据的操作,类似 REST GET。客户端声明需要哪些字段,服务器按需返回。
Mutation
修改数据的操作,类似 REST POST/PUT/DELETE。执行后通常返回修改后的数据。
Subscription
实时数据推送,通过 WebSocket 持久连接,服务端有新数据时主动推送给订阅的客户端。
Resolver
Schema 中每个字段的数据获取函数。GraphQL 执行引擎按需调用相应的 resolver 来填充响应数据。
自省(Introspection)
GraphQL 的元查询能力,客户端可以查询 Schema 本身的结构,GraphQL Playground/Voyager 就是基于自省实现的。

安装 Apollo Server

# 创建新项目
mkdir graphql-demo && cd graphql-demo
npm init -y

# 安装依赖
npm install @apollo/server graphql

# TypeScript 项目(推荐)
npm install -D typescript @types/node ts-node
npx tsc --init

第一个 Apollo Server

// src/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// 1. Schema 定义
const typeDefs = `#graphql
  type Book {
    id: ID!
    title: String!
    author: String!
    year: Int
  }

  type Query {
    books: [Book!]!
    book(id: ID!): Book
  }
`;

// 2. 模拟数据
const books = [
  { id: '1', title: 'The Pragmatic Programmer', author: 'Hunt & Thomas', year: 1999 },
  { id: '2', title: 'Clean Code', author: 'Robert C. Martin', year: 2008 },
];

// 3. Resolvers
const resolvers = {
  Query: {
    books: () => books,
    book: (_: unknown, { id }: { id: string }) =>
      books.find(b => b.id === id),
  },
};

// 4. 启动服务器
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});
console.log(`Server ready at: ${url}`);
Apollo Sandbox

服务启动后,访问 http://localhost:4000 会自动打开 Apollo Sandbox——一个功能完整的 GraphQL IDE,支持自动补全、Schema 文档浏览、查询历史、变量编辑器等。

本章小结

本章核心要点