介绍
在本教程中,我们将深入研究 GraphQL 中的查询,以便您更好地了解如何从 GraphQL 服务器中获取数据. 我们将涵盖字段,参数,代名词,操作语法等。
田野
GraphQL 是基于对对象提出特定 fields 请求的,因此,我们不能成功地谈论查询而不谈论字段,查询是客户端使用的构造来从服务器请求特定字段。
鉴于 GraphQL 结构以便在所有请求中对一个端点进行最佳曝光,请求结构以要求特定的字段,服务器结构相同,以便响应所请求的确切字段。
考虑一个客户端希望从 API 终端请求足球玩家的情况,请求将如下结构:
1{
2 players {
3 name
4 }
5}
这是一个典型的 GraphQL 查询. 查询由两个不同的部分组成:
- 根域(玩家):包含使用负载的对象
- 使用负载(名称):客户端要求的域(s)
这是 GraphQL 不可或缺的一部分,因为服务器知道客户端正在询问哪些字段,并且总是用这些准确的数据回应。
1{
2 "players": [
3 {"name": "Pogba"},
4 {"name": "Lukaku"},
5 {"name": "Rashford"},
6 {"name": "Marshal"}
7 ]
8}
该字段名称
返回一个字符串类型,在这种情况下,曼联球员的名字。然而,我们不仅限于字符串:我们可以拥有所有数据类型的字段,就像根字段玩家
返回了一系列的项目一样。
例如,在我们的最后一个查询中,我们可以重新定义查询,从列表中选择一个单独的玩家,并查询有关该玩家的更多数据。
论点
GraphQL 查询允许我们将参数传输到查询字段和嵌入式查询对象中。您可以将参数传输到查询中的每个字段和每个嵌入式对象中,以进一步深化您的查询并进行多个检索。
回到我们以前的情况,比如衬衫大小
或鞋子大小
等特定玩家套件的细节,首先,我们需要通过输入一个参数id
来确定玩家从玩家列表中,然后在查询效能负载中定义我们想要的字段:
1{
2 player(id : "Pogba") {
3 name,
4 kit {
5 shirtSize,
6 bootSize
7 }
8 }
9}
在这里,我们正在请求播放器Pogba
上的所需字段,因为我们将id
参数传入查询中。 与字段一样,没有类型限制。 参数也可能有不同的类型。 与id
参数的上一个查询的结果将如下:
1{
2 "player": {
3 "name": "Pogba",
4 "kit": [
5 {
6 "shirtSize": "large",
7 "shoeSize": "medium"
8 }
9 ]
10 }
11}
这里的一个可能的陷阱是,GraphQL查询对于单个项目和项目列表看起来几乎相同. 在这些情况下,请记住,我们总是知道根据图表中定义的预期是什么。
此外,GraphQL查询是交互式的,因此您可以随意将更多字段添加到根字段对象中,因此,作为客户端,您可以灵活地避免循环旅行,并在单个请求中要求尽可能多的数据。
现在,如果我们想为两个玩家取出相同的字段,而不仅仅是一个呢?这就是 aliases 进入的地方。
阿利亚斯
如果您仔细看看我们最后一个例子,您会注意到结果对象字段:
1// result....
2"player": {
3 "name": "Pogba",
4 "kit": [
5 {
6 "shirtSize": "large",
7 "shoeSize": "medium"
8 }
9 ]
10}
匹配查询字段:
1// query.... has matching fields with the result
2player(id : "Pogba") {
3 name,
4 kit {
5 shirtSize,
6 bootSize
7 }
8}
但是没有论点:
1(id : "Pogba")
因此,我们不能用不同的论点直接查询相同的玩家
字段,也就是说,我们不能做这样的事情:
1{
2 player(id : "Pogba") {
3 name,
4 kit {
5 shirtSize,
6 bootSize
7 }
8 }
9 player(id : "Lukaku") {
10 name,
11 kit {
12 shirtSize,
13 bootSize
14 }
15 }
16}
我们不能做到这一点,但我们可以做的是使用代名,它们允许我们将一个字段的结果重命名为我们想要的任何东西。
1{
2 player1: player(id: "Pogba") {
3 name,
4 kit {
5 shirtSize,
6 shoeSize
7 }
8 }
9 player2: player(id: "Lukaku") {
10 name,
11 kit {
12 shirtSize,
13 shoeSize
14 }
15 }
16}
在这里,这两个玩家
字段会发生冲突,但由于我们可以将它们命名为不同的名称player1
和player2
,我们可以在一个请求中获得两个结果:
1{
2 "data": {
3 "player1": {
4 "name": "Pogba",
5 "kit": [
6 {
7 "shirtSize": "large",
8 "shoeSize": "medium"
9 }
10 ]
11 },
12 "player2": {
13 "name": "Lukaku",
14 "kit": [
15 {
16 "shirtSize": "extralarge",
17 "shoeSize": "large"
18 }
19 ]
20 }
21 }
22}
现在使用代名词,我们已经成功查询了相同的字段与不同的论点,并获得了预期的答案。
运作 Syntax
到目前为止,我们一直在使用短语操作语法,我们没有明确要求定义操作名称或类型。在生产中,使用操作名称和类型是最好的做法,以帮助您的代码库变得不那么模糊。
操作语法包括两个核心:
- 可能是查询、突变或订阅的
操作类型
用于描述您打算执行的操作类型. - 可能是任何有助于您与您试图执行的操作相关的
操作名称
。
现在,我们可以重写我们以前的示例,并添加一个操作类型和名称,如下:
1query PlayerDetails{
2 player(id : "Pogba") {
3 name,
4 kit {
5 shirtSize,
6 bootSize
7 }
8 }
9}
在本示例中,查询
是操作类型,PlayerDetails
是操作名称。
变量
到目前为止,我们一直在直接将所有参数传入查询字符串中。在大多数情况下,我们传递的参数都是动态的。例如,假设客户端想要的细节来自文本输入表格或下载菜单。我们传入查询字符串的参数必须是动态的,而为了做到这一点,我们需要使用 _variables。
考虑到我们最后一个例子,如果我们想使播放器动态,以便返回选定的播放器的详细信息,我们将不得不将播放器的ID值存储在变量中,并将其传输到操作名称和查询参数中,如下:
1query PlayerDetails ($id: String){
2 player (id : $id) {
3 name,
4 kit {
5 shirtSize,
6 bootSize
7 }
8 }
9}
在这里,‘$title: String’是变量定义,而‘title’是变量名称,前缀为‘$’,然后是类型,在这种情况下是 String,这意味着我们可以避免手动插入字符串来构建动态查询。
碎片
看看我们的查询,你会注意到玩家
字段几乎是相同的,对于两个玩家:
1name,
2 kit {
3 shirtSize,
4 bootSize
5 }
为了更高效地处理我们的查询,我们可以将这个共享逻辑提取到玩家
领域的可重复使用的片段中,如下:
1{
2 player1: player(id: "Pogba") {
3 ...playerKit
4 }
5 player2: player(id: "Lukaku") {
6 ...playerKit
7 }
8}
9
10fragment playerKit on player {
11 name,
12 kit {
13 shirtSize,
14 shoeSize
15 }
16}
提取一块共享代码并在多个领域中重复使用的能力是一个关键概念,可以帮助开发人员避免在开发中甚至在生产中重复自己。
指令
GraphQL 指令为我们提供了告诉服务器在响应我们的查询时是否要包括
或跳过
特定字段的方法,在 GraphQL 中有两个内置指令可以帮助我们实现这一目标:
@skip
:当输入的值是真的时跳过特定字段@include
:当输入的值是真的时包含特定字段
让我们添加一个Boolean
指令,然后在服务器上跳过@skip
指令:
1query PlayerDetails ($playerShirtDirective: Boolean!){
2 player(id: "Pogba") {
3 name,
4 kit {
5 shirtSize @skip(if: $playerShirtDirective)
6 bootSize
7 }
8 }
9}
接下来我们要做的就是在我们的查询变量中创建playerShirtDirective
指令,并将其设置为真:
1// Query Variables
2{
3 "itemIdDirective": true
4}
这将现在返回无衬衫尺寸
的可用性:
1"player": {
2 "name": "Pogba",
3 "kit": [
4 {
5 "shoeSize": "medium"
6 }
7 ]
8}
我们可以使用@include
指令来扭转这种情况,它与@skip
指令相反,您可以使用它来通过在查询中代替@skip
指令以@include
来扭转服务器上的这种跳过操作。
结论
在本文中,我们讨论了 GraphQL 查询的细节。
如果您想了解更多关于 GraphQL 的信息,请参阅 GraphQL 官方文件. 如果您想尝试使用 GraphQL 创建项目,请尝试我们的 如何在 Ubuntu 18.04 上使用 Node.js 和 MongoDB 构建和部署 GraphQL 服务器教程。