[译] 怎样使用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 的工具实现上述策略,其他语言也有类似的工具。

相关推荐