持久化查询
最小化请求延迟的同时确保图形安全
Apollo支持两个独立但相关的功能,称为自动持久查询(APQs)和持久查询。两者结合,客户端可以通过发送操作ID而不是整个操作字符串来执行GraphQL操作。操作ID是整个操作字符串的哈希值。通过ID查询可以显著降低大操作字符串的延迟和网络带宽使用。
默认使用HTTP发送哈希查询GET
而不是默认的POST
,这使得它们更容易在网络边缘缓存。
持久查询与APQs之间的差异
持久查询功能需要将操作注册到持久查询列表(PQL)中。这使得PQL可以作为由您的其他应用程序创建的操作白名单。
在使用APQs的情况下,如果服务器找不到客户端提供的操作ID,则服务器返回一个错误,表明它需要完整的操作字符串。如果Apollo客户端收到此错误,它会自动使用完整的操作字符串重新尝试操作。
如果只想提高请求延迟和带宽使用,APQ可以解决您的用例。如果您还使用supergraph进行操作白名单,应该将操作注册到PQL。
有关持久查询和GraphOS持久查询文档中的差异的更多详细信息。
实现步骤
持久查询和APQs都需要您配置客户端的请求方式。如果您打算使用持久查询进行白名单,您还需要生成和发布一个操作清单。
在实现时,我们建议您遵循以下顺序
实现步骤 | 是否需要PQs? | 是否需要APQs? |
---|---|---|
1. 生成操作清单 | ✅ | -- |
2. 将操作清单发布到PQL | ✅ | -- |
3. 在客户端请求时启用持久查询 | ✅ | ✅ |
本文的其余部分将详细介绍这些步骤。
持久化查询还要求您创建和链接一个PQL,并配置您的路由器以接收持久化查询请求。本文档仅描述客户端创建操作清单并发送持久化查询请求所需的步骤。有关持久化查询其他配置方面的更多信息,请参阅GraphOS持久化查询文档。
0. 要求
您可以使用APQs与以下版本的Apollo Kotlin,Apollo Server和 ApolloRouter:
- Apollo Kotlin (v1.0.0+)
- Apollo Server (v1.0.0+)
- Apollo Router (v0.1.0+)
注意:您可以使用Apollo Server或Apollo Router进行 APQs。它们不需要一起使用。
使用持久化查询进行白名单的要求如下:
- Apollo Kotlin (v3.8.2+)
- Apollo Router (v1.25.0+)
- GraphOS Enterprise 计划
1. 生成操作清单
此步骤仅适用于使用持久化查询实施白名单。对于 APQs,此步骤不需要。
操作清单充当 Apollo Router 可以检查传入请求的受信任操作的黑白名单。要生成操作清单,请在您的 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-id@variant-name。
- 使用
--manifest
选项提供您要发布的清单文件的路径。
ⓘ 注意
执行持久查询发布
命令时,假设清单文件以Apollo客户端工具生成的格式生成。此命令还可以支持Relay编译器生成的清单文件,通过添加--manifest-format relay
参数来实现。您的Rover CLI版本必须为0.19.0或更高版本才能使用此参数。
执行持久查询发布
命令将执行以下操作:
将提供的清单文件中的所有操作发布到与指定变种或指定的PQL链接的PQL。
- 将清单发布到PQL是累加的。PQL中存在的任何现有条目都将保留。
- 如果您将具有相同
id
但与PQL中现有条目不同的详细信息发布的操作,整个发布命令将会因错误而失败。
更新所有其他变种,以便与这些变种关联的路由器可以获取其更新的PQL。
与生成清单一样,最好在您的CI/CD管道中执行此命令,以便将新的操作作为应用发布过程的一部分发布。提供的API密钥必须具有角色,并为本应具有Graph Admin或持久化查询发布者。持久化查询发布者是一个专门用于与rover persisted-queries publish命令一起使用的角色;具有此角色的API密钥对GraphOS中的图数据没有其他访问权限,并且适合与受信任的第三方客户端开发者共享,以便允许将操作发布到您的图的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://...").autoPersistedQueries().build()
一旦在您的ApolloClient上启用了APQ,默认情况下会发送哈希查询。
您可能希望禁用某些查询的自动持久化查询,例如,以避免数据经常更新时的任何缓存。为此,请在ApolloCall
上设置enableAutoPersistedQueries
为false:
apolloClient.query(myQuery).enableAutoPersistedQueries(false).toFlow()
为持久化查询生成自定义ID
默认情况下,Apollo使用Sha256
哈希算法来生成查询的ID。要提供自定义ID生成逻辑,请使用operationIdGenerator
选项。它接受一个实现OperationIdGenerator
接口(com.apollographql.apollo3.compiler.OperationIdGenerator
)的instance
作为输入。您可以使用此选项指定不同的哈希算法或从不同的位置获取持久化查询ID,例如,从服务或CLI。
示例MD5哈希生成器
apollo {service("service") {operationIdGenerator.set(object : com.apollographql.apollo3.compiler.OperationIdGenerator {override val version = "my-md5-version1"override fun apply(operationDocument: String, operationName: String): String {return operationDocument.md5()}})}}
import com.apollographql.apollo3.compiler.OperationIdGeneratorapollo {service("service") {operationIdGenerator = new OperationIdGenerator() {String apply(String operationDocument, String operationName) {return operationDocument.md5()}/*** Use this version override to indicate an update to the implementation.* This forces gradle to recompile models.*/String version = "my-md5-v1"}}}
标识符生成器版本控制
标识符生成器的结果是缓存的。当标识符生成器的实现发生变化时,缓存不会被更新。要表示标识符生成器实现的更新,根据上述示例更改 版本
覆盖。