[译] 怎样使用GraphQL - 进阶 - 2.服务端
英文原版地址:https://www.howtographql.com/...
GraphQL通常被认为是前端的API技术,因为它使客户端能够以更好的方式获取数据。 但是既然是API,当然是在服务器端实现的。 因为GraphQL使服务器开发人员能够专注于描述数据,而不是实现和优化特定的接口,所以在服务器上也有很多好处。
GraphQL执行
GraphQL通过定义Schema和查询语言,来从Schema中检索数据,但却不仅仅是这一种方式。更是一种实际的执行算法,用于将这些查询转换为结果。 该算法的核心非常简单:查询逐字段遍历,为每个字段执行“解析器”。 让假设我们有以下模式:
type Query { author(id: ID!): [Author] } type Author { posts: [Post] } type Post { title: String content: String }
下面是我们使用该Schema发送到服务器的查询:
query { author(id: "abc") { posts { title content } } }
首先要关注的是查询中的每个字段都可以与一个类型相对应:
query: Query { author(id: "abc"): Author { posts: [Post] { title: String content: String } } }
现在,我们可以轻松地找到并运行服务器中每个字段对应的解析器。从查询类型开始执行,并以外层为先。这意味着我们先运行Query.author
的解析器。然后,我们将该解析器的结果传递给它的子解析器,Author.posts
的解析器。在下一级,结果是一个列表,在这种情况下,算法会依次在每个元素上执行一次。所以最终执行工作如下:
Query.author(root, { id: 'abc' }, context) -> author Author.posts(author, null, context) -> posts for each post in posts Post.title(post, null, context) -> title Post.content(post, null, context) -> content
最终,执行算法将所有结果数据正确的放在定义好的结构中,并返回。
需要注意的是,大多数GraphQL服务器实现将提供“默认解析器” - 因此您不必为每个单个字段指定解析器函数。例如,在GraphQL.js中,当解析器的父对象包含具有正确名称的字段时,不需要指定解析器。
在Apollo博客上的“GraphQL Explained“文章中,可更深入的了解GraphQL执行情况。
批量解析
你可能会注意到上述执行策略的一件事是,它有点幼稚。例如,如果你有从后端API或数据库提取的解析器,则在执行一个查询期间可能会多次调用该后端。让我们假设,我们想获取几个帖子的作者,就像这样:
query { posts { title author { name avatar } } }
如果这些是博客上的帖子,很可能很多帖子将有相同的作者。所以如果我们需要一个API调用来获取每个作者对象,我们可能会意外地为同一个对象发出多个请求。例如:
fetch('/authors/1') fetch('/authors/2') fetch('/authors/1') fetch('/authors/2') fetch('/authors/1') fetch('/authors/2')
我们如何解决这个问题?让我们聪明一点。我们可以将fetch函数封装在一个工具函数中,该实函数将等待所有的解析器运行后,再确保只fetch每个元素一次:
authorLoader = new AuthorLoader() // Queue up a bunch of fetches authorLoader.load(1); authorLoader.load(2); authorLoader.load(1); authorLoader.load(2); // Then, the loader only does the minimal amount of work fetch('/authors/1'); fetch('/authors/2');
我们能做得更好吗?当然,如果我们的API支持批量请求,我们只能对后端执行一次提取操作,如下所示:
fetch('/authors?ids=1,2')
这也可以封装在上面的工具函数中。
在JavaScript中,可以使用 DataLoader 的工具实现上述策略,其他语言也有类似的工具。