GraphQL 入门: 简介
GraphQL 入门: Apollo Client - 简介
GraphQL 入门: Apollo Client - 安装和配置选项
GraphQL 入门: Apollo Client - 连接到数据
GraphQL 入门: Apollo Client - 网络层
GraphQL 入门: Apollo Client - 开发调试工具
GraphQL 入门: Apollo Client - 持久化GraphQL查询概要
GraphQL 入门: Apollo Client - 存储API
GraphQL 入门: Apollo Client - 查询(Batching)合并
摘要: 本文采用 Elixir 语言开发的 Absinthe 作为 GraphQL 的服务器端实现, 使用 Javascript 语言开发的 Apollo Client 作为 GraphQL 的客户端实现.
1. 持久化查询的概念
持久化查询, 是一种避免客户端直接在查询请求中包含查询文档的一种方式, 客户端只需要传递给要执行查询的ID, 服务器通过ID查询到GraphQL文档, 并在服务器端执行的过程.
优缺点:
客户端发送一个巨大的查询过量消耗服务器的资源
客户端可以执行任意查询,容易导致安全问题
使用持久化查询的目的:
避免了客户端发送GraphQL文档, 减少网络流量
由于GraphQL巨大的灵活性, 这也给安全带来了挑战, 这种机制实际上就是查询白名单. 只有在白名单中的查询才是能被服务器执行的.
从直接发送查询文档转换到持久化查询的效果:
发送一个巨大的查询不再是一个问题
客户端只能查询服务器支持的限制性的GraphQL查询(通过ID)
2. 要求
客户端需要使用到 persistgraphql 工具来生成对应的查询描述文档. 用于完成查询到ID的映射关系.
服务器端也需要做同样的事情.
3. 实施方案
本节描述了服务器端和客户端的具体实现.
3.1. 服务器端
服务器端采用Elixir语言作为开发语言和运行环境
采用 Absinthe 包作为 GraphQL 查询的处理模块
服务器端使用
Absinthe.Plug.DocumentProvider.Compiled
模块读取由 persistgraphql 工具生成的extracted_queries.json
文件来达到和客户端一致.
3.1.1. 服务器端查询转换原理
服务器接收到客户端发过来的查询如下:
{
id: < 查询 ID >,
variables: < 变量JSON对象 >,
}
通过查找ID, 把查询转换为如下形式, 把ID替换为查询文本:
{
query: < GraphQL文档 >,
variables: < 变量JSON对象 >
}
3.1.2. 实现细节
创建一个文档Provider:
defmodule MyApp.ExtractedQueryProvider do
use Absinthe.Plug.DocumentProvider.Compiled
provide File.read!("/path/to/extracted_queries.json")
|> Poison.decode!
|> Map.new(fn {k, v} -> {v, k} end) # invert key/value
end
添加该文档 Provider 到 Absinthe.Plug
配置:
plug Absinthe.Plug,
schema: MyApp.Schema,
document_providers: [
Absinthe.Plug.DocumentProvider.Default,
MyApp.ExtractedQueryProvider
]
当构建服务器端项目的时候, 读取 extracted_queries.json
文件的内容, 解析, 并编译为Elixir模块, 转换为中间表示, 并执行验证. 这样的持久化查询请求就像通过ID请求一个普通的对象一样.
注意:
详细的服务器实现方法,请参考这个 Issue
另外Absinthe对持久化查询的支持需要用到最新的代码, 请使用absinthe_plug
的v1.3.0-alpha.0
及以上版本.
3.2. 客户端
本节描述了持久化查询的客户端实现.
3.2.1 基本原理
客户端通过读取 extracted_queries.json
文件, 把读取到的文件内容转换为一个JSON对象, 客户端向服务器发送请求之前, 先通过把整个GraphQL查询作为对象的字符串Key, 去找到对应的ID, 通过查找到的ID去执行GraphQL查询.
这个GraphQL查询文本
到查询ID
映射的JSON对象格式如下:
{
< print(transform(GraphQL Document)) >: < ID >,
}
Javascript 只允许字符串和数字作为键, 因此我们通过
graphql-js
模块的
最终, 客户端通过下面的伪代码执行 GraphQL 查询:
query(request: Request) {
// 在 request.query 结构中查找对应的ID
// 找到对应的ID
// 通过GraphQL查询变量把ID传递给服务器
// 返回一个Promise对象用于获取服务器的返回结果
}
3.2.2 实现细节
安装 persistgraphql
命令行工具:
npm install --save persistgraphql
# 或
yarn add persistgraphql
# 全局安装
yarn global add persistgraphql
persistgraphql
有两个命令行参数, 分别表示输入和输出, 输入可以是当个文件或者目录名称, 路径可以使相对或绝对路径, 安装好后, 可以执行:
persistgraphql queries.graphql
生成 extracted_queries.json
文件. 如果指定的输入是一个目录, 那么 persistgraphql 会去查找该目录下的所后缀为 .graphql
的文件. 你可以用GitHunt-React 这个项目来实际练习一下.
git clone git@github.com:apollographql/GitHunt-React.git
cd GitHunt-React
persistgraphql ui/graphql extracted_queries.json
客户的实现方法可参考资料: 使用Apollo Client持久化GraphQL查询