[译]GraphQL如何把查询转换为响应(How GraphQL turns a query into a response)
作者:互联网
在这篇文章中,我将回答一个简单的问题,GraphQL如何把查询转换为响应?
如果你对GraphQL还不熟悉,那么在阅读之前,先了解一下“How do I GraphQL?”的三分钟介绍。这样你就能从这篇文章中得到更多。
我们这篇文章中将会介绍以下内容:
- GraphQL queries - 查询
- Schema and resolve functions - 模式和解析函数
- GraphQL execution — step by step - 逐步执行
准备好了吗?让我们开始吧!
GraphQL queries
GraphQL查询结构非常简单,易于理解。请看下面的例子:
{
subscribers(publication: "apollo-stack"){
name
email
}
}
如果我们为Building Apollo构建了一个API,显而易见这个查询将会返回所有订阅了“apollo-stack”订阅者的name
和email
。以下是响应的样子:
{
subscribers: [
{ name: "Jane Doe", email: "jane@doe.com" },
{ name: "John Doe", email: "john@doe.com" },
...
]
}
注意响应的结构与查询的结构几乎相同。GraphQL的客户端非常简单,它实际上是自解释的!
但是服务端呢?会更复杂吗?
事实证明,GraphQL服务端也相当简单。在阅读完这篇文章之后,您将清楚地了解GraphQL服务器内部发生了什么,并准备好构建自己的服务器。
Schema and Resolve Functions
每个GraphQL服务器都有两个核心部分来决定它的工作方式:schema(模式) 和 resolve functions(解析函数)。
模式:模式是可以通过GraphQL服务器获取的数据模型。它定义了允许客户端进行哪些查询,可以从服务器获取什么类型的数据,以及这些类型之间的关系。例如:
在GraphQL模式语法中,如下所示:
type Author {
id: Int
name: String
posts: [Post]
}
type Post {
id: Int
title: String
text: String
author: Author
}
type Query {
getAuthor(id: Int): Author
getPostsByTitle(titleContains: String): [Post]
}
schema {
query: Query
}
译者注:在Apollo-Server2.0中,最后一节schema可以不写
这个模式非常简单:它声明应用程序有三种类型: - Author、POST 和 Query。每个查询都必须从它的一个字段开始:getAuthor 或 getPostsByTitle。你可以把它们看作是REST端点,除了更强大之外。
Author 和 Post 相互引用。你可以通过 Author 的posts
字段获取 Post,也可以通过 Post 的author
字段从获取 Author。
模式告诉服务器允许客户端进行哪些查询,以及不同类型之间的关系,但是其中有一个关键信息是不包含的:每种类型的数据来自哪里!
这就是解析函数的用途。
Resolve Functions
解析功能有点像路由。它们指定模式中的类型和字段如何连接到各种后端,解决“如何为 Author 获取数据?”和“我需要用什么参数调用哪个后端才能获得 POST 的数据?”这样的问题。
GraphQL解析函数可以包含任意代码,这意味着GraphQL服务器可以与任何类型的后端,甚至其他GraphQL服务器对话。例如,Author 类型可以存储在SQL数据库中,而 POST 可以存储在MongoDB中,甚至可以由微服务处理。
也许GraphQL最大的特点是它对客户端隐藏了所有后端复杂性。不管您的应用程序使用了多少后端,客户端只会看到一个带有应用程序简单的、自文档化API的GraphQL端点。
下面是两个解析函数的例子:
getAuthor(_, args){
return sql.raw('SELECT * FROM authors WHERE id = %s', args.id);
}
posts(author){
return request(`https://api.blog.io/by_author/${author.id}`);
}
当然,您不会将查询或url直接写入一个解析函数中,而是将其放在一个单独的模块中。但你已经明白了解析函数的使用。
Query execution — step by step
好了,现在您已经了解了模式和解析函数,让我们来看看实际查询的执行情况。
附带说明:下面的代码是GraphQL-JS的代码,它是GraphQL的JavaScript参考实现,但是在我所知道的所有GraphQL服务器中,执行模型是相同的。
在本节的末尾,您将了解GraphQL服务器如何使用模式和解析函数一起执行查询并生成所需的结果。
下面是一个与前面介绍的模式对应的查询。它获取一个作者的姓名、该作者的所有帖子以及每个帖子的作者的姓名。
{
getAuthor(id: 5){
name
posts {
title
author {
name # this will be the same as the name above
}
}
}
}
附带说明:如果仔细观察,您会注意到这个查询两次获取同一个作者的名称。我在这里这样做只是为了说明GraphQL,同时保持模式尽可能简单。
以下是服务器响应查询的三个关键步骤:
1、解析
2、验证
3、执行
Step 1: 解析查询
首先,服务器解析字符串并将其转换为AST(抽象语法树)。如果有任何语法错误,服务器将停止执行并将语法错误返回给客户端。
Step 2: 验证
一个查询在语法上可以是正确的,但仍然没有任何意义,就像下面的英语句子在语法上是正确的,但是没有任何意义:“The sand left through the idea”。
验证阶段确保在开始执行之前给定模式查询是有效的。它检查如下:
- getAuthor 是查询类型的字段吗?
- getAuthor 是否接受名为
id
的参数? - getAuthor 返回的类型上是否有
name
和posts
字段? - ...诸如此类
作为一个应用程序开发人员,您不需要担心这个部分,因为GraphQL服务器会自动完成。这与大多数RESTfulAPI形成了对比,在这种情况下,需要由开发人员来确保所有参数都是有效的。
Step 3: 执行
如果通过验证,GraphQL服务器将执行查询。
每个GraphQL查询都具有树的形状,也就是说,它从不是循环的。执行从Query
的根开始。首先,执行器调用顶层字段的解析函数-在本例中,只是 getAuthor。它等待直到所有这些解析函数返回一个值,然后以级联的方式在下一级继续。如果一个解析函数返回一个promise
,执行者将等待该promise
resolved。
这是对执行流的一段描述。我认为,当以不同的方式展示事物时,它们总是更容易理解,所以我制作了一张图表,一张表格,甚至一段视频,一步步地带你去看。
图形式的执行流程:
表形式的执行流程:
3.1: run Query.getAuthor
3.2: run Author.name and Author.posts (for Author returned in 3.1)
3.3: run Post.title and Post.author (for each Post returned in 3.2)
3.4: run Author.name (for each Author returned in 3.3)
为了方便起见,这还是上面的查询:
{
getAuthor(id: 5){
name
posts {
title
author {
name # this will be the same as the name above
}
}
}
}
在这个查询中,只有一个根字段 getAuthor 和一个值为5的参数id
。getAuthor 解析函数将执行并返回Promise。
getAuthor(_, { id }){
return DB.Authors.findOne(id);
}
// let's assume this returns a promise that then resolves to the
// following object from the database:
{ id: 5, name: "John Doe" }
当数据库调用返回时,Promise将被resolved。一旦发生这种情况,GraphQL服务器将获取此解析函数的返回值-在本例中为一个对象-并将其传递给Author上name
和posts
字段的解析函数,因为这些字段是查询中请求的字段。name
和posts
字段的解析函数并行运行。
name(author){
return author.name;
}
posts(author){
return DB.Posts.getByAuthorId(author.id);
}
name
解析函数非常简单:它只返回刚刚从 getAuthor 解析函数传递下来的Author对象的name属性。
posts
解析函数调用数据库并返回POST对象列表:
// list returned by DB.Posts.getByAuthorId(5)
[{
id: 1,
title: "Hello World",
text: "I am here",
author_id: 5
},{
id: 2,
title: "Why am I still up at midnight writing this post?",
text: "GraphQL's query language is incredibly easy to ...",
author_id: 5
}]
注意: GraphQL-JS等待列表中所有的Promise被resolved或者rejected之后才执行下一级的解析函数
因为查询请求了每个帖子的title
和author
字段,所以GraphQL并行运行四个解析函数:每个帖子的title
和author
。
title
解析函数像name
一样是微不足道的,author
解析函数与 getAuthor 的函数相同,只不过它在POST上使用author_id
字段,而 getAuthor 函数使用id
参数:
author(post){
return DB.Authors.findOne(post.author_id);
}
最后,GraphQL执行器再一次调用Author的name
解析函数,这一次使用POSTS的author
解析函数返回的Author对象。它执行了两次—— 每个帖子执行一次。
到这里执行部分已经结束了!剩下要做的就是将结果传递到查询的根目录,并返回结果:
{
data: {
getAuthor: {
name: "John Doe",
posts: [
{
title: "Hello World",
author: {
name: "John Doe"
}
},{
title: "Why am I still up at midnight writing this post?",
author: {
name: "John Doe"
}
}
]
}
}
}
注意:这个例子稍微简化了一些。真正的生产GraphQL服务器将使用批处理和缓存来减少对后端的请求数量,并避免产生冗余的请求,比如获取同一作者两次。但这是另一篇文章的主题!
结语
如你所见,一旦你深入到它,GraphQL是非常容易理解的!我认为GraphQL在解决诸如联表、过滤、参数验证、文档等传统RESTfulAPI中很难解决的问题上,是非常出色的。
当然,GraphQL比我在这里写的要多得多,但这是以后文章的主题!
如果这让您对自己尝试GraphQL感兴趣,您应该查看我们的GraphQL server tutorial,或者阅读有关 using GraphQL on the client together with React + Redux.的相关内容。
2018年更新:理解使用Apollo Engine执行GraphQL
自从Jonas撰写这篇文章以来,我们还构建了一个名为Apollo Engine的服务,通过提供以下功能帮助开发人员了解和监视其GraphQL服务器中发生的事情:
如果您有兴趣看到您的GraphQL查询在实际应用中的执行,您可以在这里登录并检测您的服务器。如果您有兴趣支持使用GraphQL运行高性能的现代应用程序,我们可以帮助您!让我们知道。
标签:name,author,turns,into,查询,GraphQL,解析,id 来源: https://www.cnblogs.com/qianduanwriter/p/11800072.html