文档
GraphQL API 设计入门教程
第一章:GraphQL vs REST
REST 的痛点
假设要渲染一个博客首页,需要:
GET /posts— 获取文章列表GET /users/1— 获取每篇文章的作者头像GET /posts/1/comments— 获取评论数
三次请求 + 大量无关字段。这叫 Under-fetching 和 Over-fetching 双重困境。
GraphQL 的答案
query {
posts {
title
author { name avatar }
commentCount
}
}
一次请求,精确字段,不多不少。
第二章:Schema 定义语言(SDL)
标量类型
Int、Float、String、Boolean、ID
对象类型
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),
第五章:最佳实践
- 错误处理:返回
errors数组而非 HTTP 状态码 - 分页:使用 Relay Cursor Connections 规范
- 限流:限制查询深度(防止递归炸服务端)
- 持久化查询:客户端上传查询 hash,服务端只执行已知查询
思考题
- GraphQL 的 POST 请求(body 带 query)和 REST POST 有什么本质不同?
- 为什么 GraphQL 缓存比 REST 复杂?CDN 缓存还能用吗?
- 什么场景不适合用 GraphQL?(提示:简单 CRUD、文件上传、流式响应)