0%

如何使用GraphQL-进阶教程:服务端

接上篇 —— 如何使用 GraphQL-进阶教程:客户端 —— 继续翻译How to GraphQL 系列教程

了解 GraphQL 服务端的主要概念,如何执行带有解析器功能的 GraphQL 查询以及批处理多个请求。

基础和进阶系列翻译已完成:

  1. 介绍
  2. GraphQL 比 REST 的优点
  3. 核心概念
  4. 架构
  5. 客户端
  6. 服务端
  7. 更多概念
  8. 工具和生态
  9. 安全
  10. 常见问题

GraphQL 通常被解释为针对前端的 API 技术,因为它使客户端能够以比以前更好的方式获取数据。但是,API 本身当然是在服务端实现的。对服务端来说也有很多好处,因为 GraphQL 使服务端开发者可以专注于描述可用数据,而不是实现和优化特定端点。

GraphQL 执行

GraphQL 不仅指定一种描述 schema 的方法和一种从 schema 中检索数据查询语言,还指定了用于将这些查询转换为结果的实际执行算法。该算法的核心非常简单:逐字段遍历查询,为每个字段执行“解析器(resolvers)”。如下,假设我们具有以下 schema:

1
2
3
4
5
6
7
8
9
10
11
12
type Query {
author(id: ID!): Author
}

type Author {
posts: [Post]
}

type Post {
title: String
content: String
}

以下是我们发送到具有该 schema 的服务端的查询:

1
2
3
4
5
6
7
8
query {
author(id: "abc") {
posts {
title
content
}
}
}

首先要看的是查询中的每个字段都可以与一个类型相关联:

1
2
3
4
5
6
7
8
query: Query {
author(id: "abc"): Author {
posts: [Post] {
title: String
content: String
}
}
}

现在,我们可以轻松地在服务端中找到要在每个字段运行的解析器。执行从查询类型开始而且广度优先。这意味着我们首先运行 Query.author 的解析器。然后获取该解析器的结果,并将其传递给其子级,即 Author.posts 的解析器。再下一级的结果是一个列表,因此在本例中,执行算法一次只能运行在一个项目上。如下所示:

1
2
3
4
5
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” post中详细了解 GraphQL 执行。

批量解析

你可能会注意到上述执行策略有些幼稚。例如,如果有一个从后端 API 或数据库中获取的解析器,则在执行一个查询期间,该后端可能会被调用多次。比如想要获得几篇文章的作者,如下所示:

1
2
3
4
5
6
7
8
9
query {
posts {
title
author {
name
avatar
}
}
}

如果这些是博客上的帖子,则很多帖子可能具有相同的作者。因此,如果我们需要进行 API 调用以请求每个作者对象,则可能会意外地对同一个对象发出多个请求。例如:

1
2
3
4
5
6
fetch('/authors/1');
fetch('/authors/2');
fetch('/authors/1');
fetch('/authors/2');
fetch('/authors/1');
fetch('/authors/2');

这个问题的解决方式就是让请求更智能。我们可以将请求函数包装在一个工具函数中,该工具程函数将等待所有解析器运行,然后确保每个项目仅请求一次:

1
2
3
4
5
6
7
8
9
10
11
authorLoader = new AuthorLoader();

// 请求排队
authorLoader.load(1);
authorLoader.load(2);
authorLoader.load(1);
authorLoader.load(2);

// 然后这个加载器将工作最简化
fetch('/authors/1');
fetch('/authors/2');

可以做得更好吗?可以的,如果 API 支持批处理请求,那么只需对后端执行一次请求,如下所示:

1
fetch('/authors?ids=1,2');

这也可以封装在上面的加载器中。

在 JavaScript 中,可以使用名为DataLoader的工具函数来实现上述策略,并且对于其他语言也有类似的实用工具。


前端记事本,不定期更新,欢迎关注!


👆 全文结束,棒槌时间到 👇