持久查询
最小化请求延迟以保护您的图
Apollo 支持两个相互关联但独立的特性,称为 自动持久化查询 (APQs) 和 持久化查询。使用这两个特性,客户端可以通过发送操作 ID 而不是完整的操作字符串来执行 GraphQL 操作。操作 ID 是整个操作字符串的哈希。通过 ID 查询可以显著降低大操作字符串的延迟和带宽使用。
哈希查询默认通过 HTTP 发送GET
而不是默认的 POST
,使得它们更容易在边缘网络中缓存。
持久化查询和 APQs 之间的区别
持久查询功能需要将操作注册到持久查询列表(PQL)中,这样PQL就可以作为由您的-first-party应用创建的操作白名单。因此,持久查询不仅是性能特性,也是一个安全特性。
使用APQs时,如果服务器找不到客户端提供的操作ID,服务器会返回错误,提示需要完整的操作字符串。如果Apollo客户端收到此错误,它将自动使用完整的操作字符串重试该操作。
如果您只想提高请求延迟和带宽使用率,APQ可以满足您的需求。如果您还想使用操作白名单来保护您的supergraph,则应在PQL中注册操作。
关于持久查询与APQ之间的区别的更多详细信息,请参见GraphOS持久查询文档。
实现步骤
持久查询和APQs都要求您配置客户端如何发出请求。如果您打算使用持久查询进行白名单管理,您还需要生成和发布一个操作列表。
我们建议您按照以下顺序进行实现
实现步骤 | 是否需要用于PQs? | 是否需要用于APQs? |
---|---|---|
1. 生成操作列表 | ✅ | -- |
2. 将操作列表发布到PQL | ✅ | -- |
3. 在客户端请求时启用持久查询 | ✅ | ✅ |
本文的其余部分将详细介绍这些步骤。
持久查询还要求您创建和链接一个PQL,以及配置您的router以接收持久查询请求。本文件仅描述客户端创建操作的清单并发送持久查询请求所需采取的步骤。有关持久查询其他配置方面的更多信息,请参阅GraphOS持久查询文档。
0. 要求
您可以使用Apollo Kotlin、Apollo Server 和Apollo Router Core的以下版本使用APQs:
- Apollo Kotlin (v1.0.0+) }}
- Apollo Server (v1.0.0+) }}
- Apollo Router Core (v0.1.0+) }}
ⓘ 请注意
您可以使用 任选一种 Apollo Server 或者 Apollo Router Core 用于 APQs。它们不必一起使用。
对于使用持久查询进行白名单设置,有以下要求:
- Apollo Kotlin(v3.8.2+)
- GraphOS 路由器(v1.25.0+)
- GraphOS 企业计划
1. 生成操作清单
此步骤仅在实施使用持久查询进行的白名单时需要。它对 APQs 是非必需的。
操作清单作为信任操作的白名单,供GraphOS 路由器验证传入请求使用。要生成操作清单,请将您的 Gradle 脚本中的 operationManifestFormat
设置为 "persistedQueryManifest"
:
// build.gradle.ktsapollo {service("api") {packageName.set("com.example")// Enable generation of the operation manifestoperationManifestFormat.set("persistedQueryManifest")}}
操作清单会在代码生成时生成。这会在每次构建项目时自动发生,或者您可以通过执行 generateApolloSources
Gradle 任务来手动触发。
操作清单生成在 build/generated/manifest/apollo/$serviceName/persistedQueryManifest.json
中,其中 $serviceName
在这里是 "api"
。生成的操作清单看起来可能像这样:
{"format": "apollo-persisted-query-manifest","version": 1,"operations": [{"id": "e0321f6b438bb42c022f633d38c19549dea9a2d55c908f64c5c6cb8403442fef","body": "query GetItem { thing { __typename } }","name": "GetItem","type": "query"}]}
2. 发布操作规范
此步骤仅在实施使用持久查询进行的白名单时需要。它对 APQs 是非必需的。
💡 提示
请确保您的 Rover CLI 版本为 0.17.2
或更高版本。旧版本的 Rover 不支持将操作发布到 PQL。下载最新版本。
在您 生成操作规范 后,可以使用 Rover CLI 将其发布到您的 PQL,如下所示:
rover persisted-queries publish my-graph@my-variant \--manifest ./persisted-query-manifest.json
- 参数
my-graph@my-variant
是PQL关联的任何变体的图引用。graph ref
,PQL被连接到。- 图引用具有以下格式
graph-id@variant-name
。
- 图引用具有以下格式
- 使用
--manifest
选项提供要发布的清单路径。
ⓘ 请注意
持久查询发布--manifest-format relay
参数来支持由Relay编译器生成的清单。您必须使用0.19.0或更高版本的Rover CLI才能使用此参数。
持久查询发布命令执行以下操作:
将提供清单文件中的所有操作发布到与指定变体或指定PQL链接的PQL,或发布到指定的PQL。
- 将清单发布到PQL是累加的。PQL中保留任何现有条目。
- 如果您发布了一个具有相同
id
但详细信息不同的操作,该操作与PQL中现有条目相同,则整个发布命令将以错误失败。
更新PQL应用到的任何其他变体,以便与这些变体关联的路由可以获取其更新的PQL。
和 生成清单一样,最好在您的 CI/CD 流中执行此命令,以便将新的操作作为您应用程序发布流程的一部分发布。您提供给 Rover 的 API 密钥必须具有 角色,即 Graph Admin 或 Persisted Query Publisher。 Persisted Query Publisher 是一种特殊角色,专为与 rover persisted-queries publish
命令配合使用而设计;具有此角色的 API 密钥没有其他权限访问您 graph 中的数据,并且适合与可以允许发布操作到您的图 PQL 但不应有其他访问您的图权限的受信任第三方客户端开发者共享。
测试操作
您可以通过发送一些测试操作来测试您是否已成功发布您的清单:
首先,启动您的 GraphOS-连接的路由器:
APOLLO_KEY="..." APOLLO_GRAPH_REF="..." ./router --config ./router.yaml2023-05-11T15:32:30.684460Z INFO Apollo Router v1.18.1 // (c) Apollo Graph, Inc. // Licensed as ELv2 (https://go.apollo.dev/elv2)2023-05-11T15:32:30.684480Z INFO Anonymous usage data is gathered to inform Apollo product development. See https://go.apollo.dev/o/privacy for details.2023-05-11T15:32:31.507085Z INFO Health check endpoint exposed at http://127.0.0.1:8088/health2023-05-11T15:32:31.507823Z INFO GraphQL endpoint exposed at http://127.0.0.1:4000/ 🚀
然后,使用 curl
发送 POST 请求,例如:
curl https://127.0.0.1:4000 -X POST --json \'{"extensions":{"persistedQuery":{"version":1,"sha256Hash":"dc67510fb4289672bea757e862d6b00e83db5d3cbbcfb15260601b6f29bb2b8f"}}}'
如果您的路由器的 PQL 中包含一个与提供的 sha256Hash
属性值匹配的 ID 操作,它将执行相应的操作并返回其结果。
3. 在 ApolloClient
上启用持久查询
一旦您已配置代码生成以包含操作 ID,您可以通过操作 ID 而不是完整的操作字符串来更新您的客户端以 查询。此配置无论您使用 APQ 还是持久查询都相同。在您的 ApolloClient.Builder
上调用 autoPersistedQueries()
:
val apolloClient = ApolloClient.Builder().serverUrl("https://example.com/graphql").autoPersistedQueries().build()
一旦您的 ApolloClient 上启用了 APQ,默认情况下将发送哈希查询。
您可能想禁用某些查询的 自动持久查询,例如,避免在数据经常更新时的任何缓存。为此,在 ApolloCall
上将 enableAutoPersistedQueries
设置为 false:
apolloClient.query(myQuery).enableAutoPersistedQueries(false).toFlow()
为持久查询生成自定义 ID
默认情况下,Apollo 使用 Sha256
哈希算法为查询生成 ID。要提供自定义 ID 生成逻辑,请使用 Apollo 编译器插件。
插件interface
有一个可以覆盖以自定义操作operationIds()
id的方法。它接受一个包含源OperationDescriptor
文档的列表,包括名称和id。它返回一个将名称映射到生成的id的OperationId
列表。
示例:MD5散列生成器
class MyPlugin: Plugin {override fun operationIds(descriptors: List<OperationDescriptor>): List<OperationId> {return descriptors.map { OperationId(it.source.md5(), it.name) }}}