GraphQL 最佳实践
GraphQL 已足够成熟,在社区中流行多年,众多分享最佳实践的文章相继发布。这些指南涵盖了 GraphQL 的几乎所有方面,包括 schema 设计、命名规范、安全处理以及提供有意义的错误信息等。
以下是一些关于 GraphQL 最佳实践最具说服力的指南。
graphql.org 上的最佳实践
GraphQL 官方网站提供了 GraphQL 最佳实践 的通用介绍。
这些内容主要涵盖顶层关注点,例如:
- 对结果进行分页 的最佳方式
- GraphQL 层在架构中所处的位置
- 如何使用 Node 接口实现 全局对象标识
- 如何 缓存结果
- 以及其他许多内容

Lee Byron 的建议
GraphQL 向世界发布后不久,GraphQL 的创始人 Lee Byron 发表了文章 Lessons From 4 Years of GraphQL,阐述了我们在概念上应如何使用 GraphQL:
- 命名很重要
- 以图而非端点的方式思考
- 描述数据,而非视图
- GraphQL 是一个轻薄的接口
- 隐藏实现细节
他还详细介绍了在 Facebook 使用 GraphQL 过程中所学到的宝贵原则和经验。
GraphQL Rules
GraphQL Rules 是一个专门介绍 GraphQL 日常最佳实践的网站,主要涉及 GraphQL schema 的设计。
该资源内容非常详尽。它汇集了若干优秀资源的信息(例如文章 Designing GraphQL Mutations 以及 Shopify 的教程 Designing a GraphQL API),并将其简洁地呈现在一起。
所描述的规则包括:
- 命名规则
- GraphQL 字段和参数使用
camelCase。 - GraphQL 类型使用
UpperCamelCase。 - ENUM 类型名称使用
CAPITALIZED_WITH_UNDERSCORES。
- GraphQL 字段和参数使用
- 类型规则
- 如需声明具有特定语义值的字段或参数,请使用自定义标量类型。
- 对包含特定值集合的字段使用 Enum。
- 字段规则(Output)
- 为字段使用语义化名称,避免在字段名中泄露实现细节。
- 如果字段始终具有给定值,则使用
Non-Null字段。 - 尽可能将相关字段归入自定义 Object 类型。
- 参数规则(Input)
- 将耦合的参数归入新的 input-type。
- 为参数使用严格的标量类型,例如使用
DateTime而非String。 - 对于 query 执行所必需的参数,将其标记为
required。
- 列表规则
- 过滤列表时,使用包含所有可用过滤器的
filter参数。 - 列表排序使用
Enum或[Enum!]类型的sort参数。 - 使用带默认值的
limit和skip来限制列表中返回的条目数。 - 分页使用
page、perPage参数,并返回包含items(元素数组)和pageInfo(元数据)的 output 类型。 - 对于无限滚动列表,使用 Relay Cursor Connections Specification。
- 过滤列表时,使用包含所有可用过滤器的
- Mutation 规则
- 使用 Namespace-type 对单个资源内的 mutation 进行分组。
- 超越 CRUD——为资源上的不同业务操作创建小型 mutation。
- 考虑对多个条目执行 mutation 的能力(同类型批量变更)。
- Mutation 应清晰描述所有必填参数,不应存在非此即彼的选项。
- 在 mutation 中,将所有变量放入一个唯一的 input 参数。
- 每个 mutation 应具有唯一的 payload 类型。
Resolver 最佳实践
文章 GraphQL Resolvers: Best Practices 介绍了如何最优地创建字段 resolver。尽管该文章面向 Node.js 服务器(PayPal 的基础设施基于 Express),但其中许多经验同样适用于其他技术,包括 PHP。
主要收获如下:
- 谨慎使用从父级到子级的数据获取与传递。
- 使用 dataloader 等库对下游请求去重。
- 注意对数据源造成的压力。
- 不要修改 "context",以确保代码一致且少出错。
- 编写可读、可维护、可测试的 resolver,不要过于复杂。
- 尽量使 resolver 保持轻薄,将数据获取逻辑抽取为可复用的异步函数。
OWASP - GraphQL Cheat Sheet
OWASP(Open Web Application Security Project)是一个致力于提升软件安全性的非营利基金会。它研究不同技术对漏洞利用的脆弱性,并详细描述安全问题的解决方案,帮助开发者更轻松地防范攻击。
OWASP 发布了 GraphQL Cheat Sheet,说明了 GraphQL 中最常见的攻击和最大的安全问题,以及如何应对这些问题。
针对 GraphQL 的常见攻击包括:
- 注入攻击——通常包括但不限于:
- SQL 和 NoSQL 注入
- OS 命令注入
- SSRF 和 CRLF 注入/请求走私
- DoS(拒绝服务)
- 滥用已损坏的授权:包括不当或过度访问,以及 IDOR
- 批量攻击(Batching Attacks),一种 GraphQL 特有的暴力破解方法
- 滥用不安全的默认配置
OWASP 随后提供了避免上述每种攻击的建议。
GraphQL Query 最佳实践
Apollo 团队发布了 GraphQL query 最佳实践,提供了构建 GraphQL query 的实用见解。
例如,以下两个 query 实现了相同的目标,但由于第一个 query 有操作名称,在调试时更易理解和使用:
# Recommended ✅
query GetBooks {
books {
title
}
}
# Not recommended ❌
query {
books {
title
}
}其建议包括:
- 为所有操作命名
- 使用变量提供 GraphQL 参数
- 在需要的地方只 query 所需的数据
- 使用 fragment 封装相关字段集合
- 分别 query 全局数据和用户特定数据
充分利用统一图
同样来自 Apollo 团队的网站 Principled GraphQL 阐述了 GraphQL 不仅仅是一种规范,更重要的是,它是与公司数据"图"进行交互的接口。
该网站通过 10 条原则,说明如何最大限度地利用单一图:
- One Graph:企业应拥有一个统一的图,而非各团队各自创建的多个图。
- Federated Implementation:虽然只有一个图,但该图的实现应跨多个团队进行联邦管理。
- Track the Schema in a Registry:应有单一的真实来源来注册和追踪图。
- Abstract, Demand-Oriented Schema:schema 应作为抽象层,在向消费者提供灵活性的同时隐藏服务实现细节。
- Use an Agile Approach to Schema Development:schema 应基于实际需求逐步构建,并随时间平滑演进。
- Iteratively Improve Performance:性能管理应是一个持续的数据驱动过程,平滑适应不断变化的 query 负载和服务实现。
- Use Graph Metadata to Empower Developers:开发者在整个开发过程中应对图有丰富的感知。
- Access and Demand Control:按客户端授予图的访问权限,并管理客户端可以访问什么以及如何访问。
- Structured Logging:捕获所有图操作的结构化日志,并将其作为了解图使用情况的主要工具。
- Separate the GraphQL Layer from the Service Layer:采用分层架构,将图功能分离为独立层,而非内嵌于每个服务中。