资源
资源GraphQL 最佳实践

GraphQL 最佳实践

GraphQL 已足够成熟,在社区中流行多年,众多分享最佳实践的文章相继发布。这些指南涵盖了 GraphQL 的几乎所有方面,包括 schema 设计、命名规范、安全处理以及提供有意义的错误信息等。

以下是一些关于 GraphQL 最佳实践最具说服力的指南。

graphql.org 上的最佳实践

GraphQL 官方网站提供了 GraphQL 最佳实践 的通用介绍。

这些内容主要涵盖顶层关注点,例如:

GraphQL 层在架构中所处的位置

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),并将其简洁地呈现在一起。

所描述的规则包括:

  1. 命名规则
    • GraphQL 字段和参数使用 camelCase
    • GraphQL 类型使用 UpperCamelCase
    • ENUM 类型名称使用 CAPITALIZED_WITH_UNDERSCORES
  2. 类型规则
    • 如需声明具有特定语义值的字段或参数,请使用自定义标量类型。
    • 对包含特定值集合的字段使用 Enum。
  3. 字段规则(Output)
    • 为字段使用语义化名称,避免在字段名中泄露实现细节。
    • 如果字段始终具有给定值,则使用 Non-Null 字段。
    • 尽可能将相关字段归入自定义 Object 类型。
  4. 参数规则(Input)
    • 将耦合的参数归入新的 input-type。
    • 为参数使用严格的标量类型,例如使用 DateTime 而非 String
    • 对于 query 执行所必需的参数,将其标记为 required
  5. 列表规则
    • 过滤列表时,使用包含所有可用过滤器的 filter 参数。
    • 列表排序使用 Enum[Enum!] 类型的 sort 参数。
    • 使用带默认值的 limitskip 来限制列表中返回的条目数。
    • 分页使用 pageperPage 参数,并返回包含 items(元素数组)和 pageInfo(元数据)的 output 类型。
    • 对于无限滚动列表,使用 Relay Cursor Connections Specification。
  6. 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 的常见攻击包括:

  1. 注入攻击——通常包括但不限于:
    • SQL 和 NoSQL 注入
    • OS 命令注入
    • SSRF 和 CRLF 注入/请求走私
  2. DoS(拒绝服务)
  3. 滥用已损坏的授权:包括不当或过度访问,以及 IDOR
  4. 批量攻击(Batching Attacks),一种 GraphQL 特有的暴力破解方法
  5. 滥用不安全的默认配置

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 条原则,说明如何最大限度地利用单一图:

  1. One Graph:企业应拥有一个统一的图,而非各团队各自创建的多个图。
  2. Federated Implementation:虽然只有一个图,但该图的实现应跨多个团队进行联邦管理。
  3. Track the Schema in a Registry:应有单一的真实来源来注册和追踪图。
  4. Abstract, Demand-Oriented Schema:schema 应作为抽象层,在向消费者提供灵活性的同时隐藏服务实现细节。
  5. Use an Agile Approach to Schema Development:schema 应基于实际需求逐步构建,并随时间平滑演进。
  6. Iteratively Improve Performance:性能管理应是一个持续的数据驱动过程,平滑适应不断变化的 query 负载和服务实现。
  7. Use Graph Metadata to Empower Developers:开发者在整个开发过程中应对图有丰富的感知。
  8. Access and Demand Control:按客户端授予图的访问权限,并管理客户端可以访问什么以及如何访问。
  9. Structured Logging:捕获所有图操作的结构化日志,并将其作为了解图使用情况的主要工具。
  10. Separate the GraphQL Layer from the Service Layer:采用分层架构,将图功能分离为独立层,而非内嵌于每个服务中。