GraphQL 规范有意对 API 面临的一些重要问题保持沉默,例如处理网络、授权和分页。这并不意味着在使用 GraphQL 时没有解决这些问题的方案,只是它们超出了对 GraphQL *是什么* 的描述,而是常见的实践。
本节中的文章不应被视为金科玉律,在某些情况下,可以合理地忽略它们,而采用其他方法。一些文章介绍了 Facebook 在设计和部署 GraphQL 服务时所发展的一些理念,而另一些文章则更侧重于解决常见问题的战术建议,例如通过 HTTP 提供服务和执行授权。
以下是 GraphQL 服务中一些更常见的最佳实践和意见立场简要描述,但是本节中的每篇文章都会更深入地介绍这些主题和其他主题。
GraphQL 通常通过 HTTP 通过单个端点提供服务,该端点表达了服务的全部功能。这与 REST API 形成对比,REST API 公开了一套 URL,每个 URL 公开单个资源。虽然 GraphQL 可以与一套资源 URL 一起使用,但这会使其更难与 GraphiQL 等工具一起使用。
阅读有关此主题的更多信息,请访问 通过 HTTP 提供服务。
GraphQL 服务通常使用 JSON 响应,但是 GraphQL 规范 不要求使用 JSON。JSON 对于承诺提高网络性能的 API 层来说似乎是一个奇怪的选择,但是因为它主要是文本,所以它可以使用 GZIP 非常有效地压缩。
鼓励任何生产 GraphQL 服务启用 GZIP 并鼓励其客户端发送标头
Accept-Encoding: gzip
JSON 对客户端和 API 开发人员来说也非常熟悉,并且易于阅读和调试。事实上,GraphQL 语法部分受到 JSON 语法的启发。
虽然没有什么可以阻止 GraphQL 服务像其他 REST API 一样进行版本控制,但 GraphQL 认为通过提供持续演进 GraphQL 架构的工具来避免版本控制。
为什么大多数 API 需要版本控制?当对从 API 端点返回的数据控制有限时,任何更改都可能被视为重大更改,而重大更改需要新版本。如果向 API 添加新功能需要新版本,那么就会出现一个权衡:频繁发布并拥有许多增量版本,还是 API 的可理解性和可维护性。
相反,GraphQL 只返回显式请求的数据,因此可以通过在这些类型上添加新类型和新字段来添加新功能,而不会造成重大更改。这导致了一种常见的做法,即始终避免重大更改并提供无版本 API。
大多数识别“null”的类型系统都提供通用类型和该类型的可空版本,其中默认类型不包含“null”,除非显式声明。但是,在 GraphQL 类型系统中,每个字段默认都是可空的。这是因为在由数据库和其他服务支持的网络服务中,可能会出现许多问题。数据库可能宕机,异步操作可能失败,可能会抛出异常。除了系统故障之外,授权通常是细粒度的,其中请求中的各个字段可能具有不同的授权规则。
通过将每个字段默认设置为可空,这些原因中的任何一个都可能导致仅返回该字段为“null”,而不是导致请求完全失败。相反,GraphQL 提供了非空类型的变体,这些变体向客户端保证,如果请求,该字段将永远不会返回“null”。相反,如果发生错误,则前面的父字段将为“null”。
在设计 GraphQL 架构时,务必牢记所有可能出现的问题,以及“null”是否适合作为失败字段的值。通常情况下,它是合适的,但有时它不合适。在这些情况下,使用非空类型来保证这一点。
GraphQL 类型系统允许某些字段返回 值列表,但将较长值列表的分页留给 API 设计者。 对于分页,存在多种可能的 API 设计,每种设计都有其优缺点。
通常,可能返回长列表的字段会接受参数“first”和“after”,以允许指定列表的特定区域,其中“after”是列表中每个值的唯一标识符。
最终,设计具有丰富功能的分页 API 导致了一种称为“连接”的最佳实践模式。 一些 GraphQL 的客户端工具,例如 Relay,了解连接模式,并且当 GraphQL API 使用此模式时,可以自动提供对客户端分页的支持。
有关此内容的更多信息,请参阅有关 分页 的文章。
GraphQL 的设计方式允许您在服务器上编写简洁的代码,其中每个类型上的每个字段都有一个专注的单一用途函数来解析该值。 但是,如果没有额外的考虑,一个简单的 GraphQL 服务可能会非常“喋喋不休”或重复地从您的数据库加载数据。
这通常通过批处理技术来解决,其中在短时间内收集来自后端的多个数据请求,然后使用 Facebook 的 DataLoader 等工具将这些请求作为单个请求分派到底层数据库或微服务。