学习教程
最佳实践

GraphQL 最佳实践

¥GraphQL Best Practices

GraphQL 规范有意对 API 面临的一些重要问题保持沉默,例如处理网络、授权和分页。这并不意味着使用 GraphQL 时这些问题没有解决方案,只是它们超出了 GraphQL 的描述范围,而只是常见做法。

¥The GraphQL specification is intentionally silent on a handful of important issues facing APIs such as dealing with the network, authorization, and pagination. This doesn’t mean that there aren’t solutions for these issues when using GraphQL, just that they’re outside the description about what GraphQL is and instead just common practice.

本节中的文章不应被视为福音,在某些情况下可能会理所当然地被忽略,而转而采用其他方法。一些文章介绍了 Facebook 内部围绕设计和部署 GraphQL 服务开发的一些理念,而另一些文章则是解决常见问题(例如通过 HTTP 提供服务和执行授权)的更具战术性的建议。

¥The articles in this section should not be taken as gospel, and in some cases may rightfully be ignored in favor of some other approach. Some articles introduce some of the philosophy developed within Facebook around designing and deploying GraphQL services, while others are more tactical suggestions for solving common problems like serving over HTTP and performing authorization.

以下是 GraphQL 服务的一些更常见的最佳实践和有态度的立场的简要描述,但是本节中的每篇文章都将更深入地讨论这些主题和其他主题。

¥Following are brief descriptions of some of the more common best practices and opinionated stances held by GraphQL services, however each article in this section will go into more depth on these and other topics.

HTTP

GraphQL 通常通过 HTTP 通过单个端点提供服务,该端点表达了服务的全套功能。这与 REST API 形成鲜明对比,REST API 公开一组 URL,每个 URL 公开一个资源。虽然 GraphQL 可以与一套资源 URL 一起使用,但这可能会使其更难以与 GraphiQL 等工具一起使用。

¥GraphQL is typically served over HTTP via a single endpoint which expresses the full set of capabilities of the service. This is in contrast to REST APIs which expose a suite of URLs each of which expose a single resource. While GraphQL could be used alongside a suite of resource URLs, this can make it harder to use with tools like GraphiQL.

请阅读 通过 HTTP 提供服务 了解更多相关信息。

¥Read more about this in Serving over HTTP.

JSON(带 GZIP)

¥JSON (with GZIP)

GraphQL 服务通常使用 JSON 进行响应,但是 GraphQL 规范 不需要它。对于确保更好的网络性能的 API 层来说,JSON 似乎是一个奇怪的选择,但由于它主要是文本,因此它使用 GZIP 的压缩效果非常好。

¥GraphQL services typically respond using JSON, however the GraphQL spec does not require it. JSON may seem like an odd choice for an API layer promising better network performance, however because it is mostly text, it compresses exceptionally well with GZIP.

我们鼓励任何生产 GraphQL 服务启用 GZIP 并鼓励其客户端发送标头:

¥It’s encouraged that any production GraphQL services enable GZIP and encourage their clients to send the header:

Accept-Encoding: gzip

JSON 对于客户端和 API 开发者来说也非常熟悉,并且易于阅读和调试。事实上,GraphQL 语法部分受到 JSON 语法的启发。

¥JSON is also very familiar to client and API developers, and is easy to read and debug. In fact, the GraphQL syntax is partly inspired by the JSON syntax.

版本控制

¥Versioning

虽然没有什么可以阻止 GraphQL 服务像任何其他 REST API 一样进行版本控制,但 GraphQL 强烈建议通过提供用于 GraphQL 结构持续发展的工具来避免版本控制。

¥While there’s nothing that prevents a GraphQL service from being versioned just like any other REST API, GraphQL takes a strong opinion on avoiding versioning by providing the tools for the continuous evolution of a GraphQL schema.

为什么大多数 API 都有版本?当对从 API 端点返回的数据的控制有限时,任何更改都可以被视为重大更改,而重大更改需要新版本。如果向 API 添加新功能需要新版本,那么就需要在经常发布和拥有许多增量版本与 API 的可理解性和可维护性之间进行权衡。

¥Why do most APIs version? When there’s limited control over the data that’s returned from an API endpoint, any change can be considered a breaking change, and breaking changes require a new version. If adding new features to an API requires a new version, then a tradeoff emerges between releasing often and having many incremental versions versus the understandability and maintainability of the API.

相比之下,GraphQL 仅返回显式请求的数据,因此可以通过新类型和这些类型上的新字段添加新功能,而无需创建重大更改。这导致了一种常见的做法,即始终避免重大更改并提供无版本 API。

¥In contrast, GraphQL only returns the data that’s explicitly requested, so new capabilities can be added via new types and new fields on those types without creating a breaking change. This has led to a common practice of always avoiding breaking changes and serving a versionless API.

可空性

¥Nullability

大多数识别 “null” 的类型系统都提供该类型的通用类型和可为 null 的版本,其中除非显式声明,否则默认类型不包括 “null”。然而,在 GraphQL 类型系统中,默认情况下每个字段都可以为空。这是因为在数据库和其他服务支持的网络服务中,有很多事情可能会出错。数据库可能会崩溃,异步操作可能会失败,可能会引发异常。除了简单的系统故障之外,授权通常可以是细粒度的,其中请求中的各个字段可以具有不同的授权规则。

¥Most type systems which recognise “null” provide both the common type and the nullable version of that type, whereby default types do not include “null” unless explicitly declared. However, in a GraphQL type system, every field is nullable by default. This is because there are many things that can go awry in a networked service backed by databases and other services. A database could go down, an asynchronous action could fail, an exception could be thrown. Beyond simply system failures, authorization can often be granular, where individual fields within a request can have different authorization rules.

通过将每个字段默认为可为空,任何这些原因都可能导致该字段仅返回 “null”,而不是请求完全失败。相反,GraphQL 提供了类型的 non-null 变体,这向客户端保证,如果请求,该字段将永远不会返回 “null”。相反,如果发生错误,则先前的父字段将改为 “null”。

¥By defaulting every field to nullable, any of these reasons may result in just that field returned “null” rather than having a complete failure for the request. Instead, GraphQL provides non-null variants of types which make a guarantee to clients that if requested, the field will never return “null”. Instead, if an error occurs, the previous parent field will be “null” instead.

在设计 GraphQL 结构时,重要的是要记住所有可能出错的问题以及 “null” 是否是失败字段的适当值。通常是这样,但有时也不是。在这些情况下,请使用非空类型来做出保证。

¥When designing a GraphQL schema, it’s important to keep in mind all the problems that could go wrong and if “null” is an appropriate value for a failed field. Typically it is, but occasionally, it’s not. In those cases, use non-null types to make that guarantee.

分页

¥Pagination

GraphQL 类型系统允许某些字段返回 值列表,但将较长值列表的分页留给 API 设计者。有多种可能的分页 API 设计,每种设计都有优点和缺点。

¥The GraphQL type system allows for some fields to return lists of values, but leaves the pagination of longer lists of values up to the API designer. There are a wide range of possible API designs for pagination, each of which has pros and cons.

通常,可以返回长列表的字段接受参数 “first” 和 “after”,以允许指定列表的特定区域,其中 “after” 是列表中每个值的唯一标识符。

¥Typically, fields that could return long lists accept arguments “first” and “after” to allow for specifying a specific region of a list, where “after” is a unique identifier of each of the values in the list.

最终设计具有功能丰富的分页的 API 产生了称为 “连接” 的最佳实践结构。一些 GraphQL 客户端工具(例如 Relay)了解连接结构,并且当 GraphQL API 采用此结构时可以自动提供对客户端分页的支持。

¥Ultimately designing APIs with feature-rich pagination led to a best practice pattern called “Connections”. Some client tools for GraphQL, such as Relay, know about the Connections pattern and can automatically provide support for client-side pagination when a GraphQL API employs this pattern.

请阅读有关 分页 的文章了解更多相关信息。

¥Read more about this in the article on Pagination.

服务器端批处理和缓存

¥Server-side Batching & Caching

GraphQL 的设计方式允许你在服务器上编写干净的代码,其中每种类型的每个字段都有一个集中的单一用途函数来解析该值。然而,如果不额外考虑,原生 GraphQL 服务可能会非常 “chatty” 或重复从数据库加载数据。

¥GraphQL is designed in a way that allows you to write clean code on the server, where every field on every type has a focused single-purpose function for resolving that value. However without additional consideration, a naive GraphQL service could be very “chatty” or repeatedly load data from your databases.

这通常通过批处理技术来解决,即在短时间内收集来自后端的多个数据请求,然后使用 Facebook 的 DataLoader 等工具将单个请求分派到底层数据库或微服务。

¥This is commonly solved by a batching technique, where multiple requests for data from a backend are collected over a short period of time and then dispatched in a single request to an underlying database or microservice by using a tool like Facebook’s DataLoader.