
关于
GraphQL 让客户端精确获取所需数据。涵盖 Schema 设计、查询优化、订阅和 API 网关模式。
name: graphql description: GraphQL 让客户端精确获取所需数据——不多不少。一个端点、类型化模式、内省。但使其强大的灵活性也使其危险。没有适当的控制,客户端可以构造使服务器崩溃的查询。 risk: safe source: vibeship-spawner-skills (Apache 2.0) date_added: 2026-02-27
GraphQL
GraphQL 让客户端精确获取所需数据——不多不少。一个端点、类型化模式、内省。但使其强大的灵活性也使其危险。没有适当的控制,客户端可以构造使服务器崩溃的查询。
本技能涵盖模式设计、解析器、用于 N+1 预防的 DataLoader、微服务联邦,以及与 Apollo/urql 的客户端集成。 关键洞察:GraphQL 是一个契约。模式就是 API 文档。请谨慎设计。
2025 年的教训:GraphQL 并非总是答案。对于简单的 CRUD,REST 更简单。对于高性能公共 API,带缓存的 REST 更胜一筹。当你有复杂的数据关系和多样化的客户端需求时,使用 GraphQL。
原则
- 模式优先设计——模式就是契约
- 使用 DataLoader 防止 N+1 查询
- 限制查询深度和复杂度
- 使用 fragments 实现可复用的选择集
- Mutations 应该是具体的,而非通用的更新操作
- 错误即数据——使用 union 类型处理预期失败
- 可空性是有意义的——有意识地设计
能力
- graphql-schema-design
- graphql-resolvers
- graphql-federation
- graphql-subscriptions
- graphql-dataloader
- graphql-codegen
- apollo-server
- apollo-client
- urql
范围
- database-queries -> postgres-wizard
- authentication -> authentication-oauth
- rest-api-design -> backend
- websocket-infrastructure -> backend
工具
服务端
- @apollo/server - 适用场景:Apollo Server v4 备注:最流行的 GraphQL 服务器
- graphql-yoga - 适用场景:轻量级替代方案 备注:适合 serverless
- mercurius - 适用场景:Fastify 集成 备注:快速,使用 JIT
客户端
- @apollo/client - 适用场景:功能完整的客户端 备注:缓存、状态管理
- urql - 适用场景:轻量级替代方案 备注:更小、更简单
- graphql-request - 适用场景:简单请求 备注:最小化,无缓存
工具
- graphql-codegen - 适用场景:类型生成 备注:TypeScript 必备
- dataloader - 适用场景:N+1 预防 备注:批处理和缓存
模式
模式设计
具有适当可空性的类型安全模式
适用场景:设计任何 GraphQL API
模式设计:
""" 模式是你的 API 契约。有意识地设计可空性—— 非空字段必须始终能解析。 """
type Query {
非空 - 始终返回用户或抛出异常
user(id: ID!): User!
可空 - 未找到时返回 null
userByEmail(email: String!): User
非空列表包含非空项
users(limit: Int = 10, offset: Int = 0): [User!]!
带分页的搜索
searchUsers( query: String! first: Int after: String ): UserConnection! }
type Mutation {
复杂 mutations 使用 Input 类型
createUser(input: CreateUserInput!): CreateUserPayload! updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload! deleteUser(id: ID!): DeleteUserPayload! }
type Subscription { userCreated: User! messageReceived(roomId: ID!): Message! }
Input 类型
input CreateUserInput { email: String! name: String! role: Role = USER }
input UpdateUserInput { email: String name: String role: Role }
Payload 类型(错误即数据)
type CreateUserPayload { user: User errors: [Error!]! }
union UpdateUserPayload = UpdateUserSuccess | NotFoundError | ValidationError
type UpdateUserSuccess { user: User! }
枚举
enum Role { USER ADMIN MODERATOR }
带关系的类型
type User { id: ID! email: String! name: String! role: Role! posts(limit: Int = 10): [Post!]! createdAt: DateTime! }
type Post { id: ID! title: String! content: String! author: User! comments: [Comment!]! published: Boolean! }
分页(Relay 风格)
type UserConnection { edges: [UserEdge!]! pageInfo: PageInfo! totalCount: Int! }
type UserEdge { node: User! cursor: String! }
type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String }
DataLoader 防止 N+1
批处理和缓存数据库查询
适用场景:解析关系
DATALOADER:
""" 没有 DataLoader,获取 10 篇文章及其作者 会产生 11 次查询(1 次获取文章 + 10 次获取每个作者)。 DataLoader 将其批处理为 2 次查询。 """
import DataLoader from 'dataloader';
// Create loaders per request
function createLoaders(db) {
return {
userLoader: new DataLoader(async (ids) => {
// Single query for all users
const users = await db.user.findMany({
where: { id: { in: ids } }
});
// Return in same order as ids
const userMap = new Map(users.map(u => [u.id, u]));
return ids.map(id => userMap.get(id) || null);
}),
};
}
