GraphQL API 设计入门

知识库
知识库文档
/tech-stacks/graphql/tutorial/GraphQL API 设计入门.md

文档

GraphQL API 设计入门教程

第一章:GraphQL vs REST

REST 的痛点

假设要渲染一个博客首页,需要:

  1. GET /posts — 获取文章列表
  2. GET /users/1 — 获取每篇文章的作者头像
  3. GET /posts/1/comments — 获取评论数

三次请求 + 大量无关字段。这叫 Under-fetchingOver-fetching 双重困境。

GraphQL 的答案

query {
  posts {
    title
    author { name avatar }
    commentCount
  }
}

一次请求,精确字段,不多不少。

第二章:Schema 定义语言(SDL)

标量类型

  • IntFloatStringBooleanID

对象类型

type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}

Query / Mutation / Subscription

type Query {
  user(id: ID!): User
  users(filter: UserFilter): [User!]!
}

第三章:Resolver 解析链

每个字段都可以有自己的 Resolver:

const resolvers = {
  Query: {
    user: (parent, args, context, info) => db.findUser(args.id),
  },
  User: {
    posts: (user) => db.findPostsByUserId(user.id),
  },
};

parent 是上一级 Resolver 的返回值,这让嵌套查询可以逐级解析、按需加载。

第四章:N+1 问题与 DataLoader

query {
  books {        # 1 次查询得到 100 本书
    author {     # 每本书再查 1 次作者 = 100 次查询!
      name
    }
  }
}

解决方案:DataLoader 批量 + 缓存。

const DataLoader = require('dataloader');

const authorLoader = new DataLoader(async (ids) => {
  const authors = await db.findAuthorsByIds(ids);
  return ids.map(id => authors.find(a => a.id === id));
});

// Book.author resolver
author: (book) => authorLoader.load(book.authorId),

第五章:最佳实践

  1. 错误处理:返回 errors 数组而非 HTTP 状态码
  2. 分页:使用 Relay Cursor Connections 规范
  3. 限流:限制查询深度(防止递归炸服务端)
  4. 持久化查询:客户端上传查询 hash,服务端只执行已知查询

思考题

  1. GraphQL 的 POST 请求(body 带 query)和 REST POST 有什么本质不同?
  2. 为什么 GraphQL 缓存比 REST 复杂?CDN 缓存还能用吗?
  3. 什么场景不适合用 GraphQL?(提示:简单 CRUD、文件上传、流式响应)

信息

路径
/tech-stacks/graphql/tutorial/GraphQL API 设计入门.md
更新时间
2026/5/31