在本页中,您将详细了解如何查询 GraphQL 服务器。
从最简单的角度来说,GraphQL 就是关于请求对象上的特定字段。让我们从一个非常简单的查询和运行它时得到的结果开始
您可以立即看到查询与结果具有完全相同的形状。这对 GraphQL 至关重要,因为您始终会得到预期的结果,并且服务器确切地知道客户端请求哪些字段。
字段 name
返回一个 String
类型,在本例中是星球大战主角的名字,"R2-D2"
。
哦,还有一件事 - 上面的查询是交互式的。这意味着您可以根据需要更改它并查看新结果。尝试在查询中的
hero
对象中添加一个appearsIn
字段,并查看新结果。
在前面的示例中,我们只请求了英雄的名字,它返回了一个字符串,但字段也可以引用对象。在这种情况下,您可以对该对象进行子选择字段。GraphQL 查询可以遍历相关对象及其字段,让客户端在一个请求中获取大量相关数据,而不是像在经典 REST 架构中那样进行多次往返。
请注意,在本例中,friends
字段返回一个项目数组。GraphQL 查询对于单个项目或项目列表看起来相同;但是,我们根据模式中指示的内容知道要期望哪一个。
如果我们唯一能做的事情是遍历对象及其字段,那么 GraphQL 已经是一种非常有用的数据获取语言。但是,当您添加向字段传递参数的能力时,事情会变得更加有趣。
在像 REST 这样的系统中,您只能传递一组参数 - 请求中的查询参数和 URL 段。但在 GraphQL 中,每个字段和嵌套对象都可以获得自己的参数集,这使得 GraphQL 成为进行多个 API 获取的完整替代方案。您甚至可以将参数传递到标量字段中,以在服务器上实现数据转换,而不是在每个客户端上单独实现。
参数可以是多种不同的类型。在上面的示例中,我们使用了一个枚举类型,它代表一组有限的选项(在本例中是长度单位,要么是METER
,要么是FOOT
)。GraphQL 带有一组默认类型,但 GraphQL 服务器也可以声明自己的自定义类型,只要它们可以序列化到您的传输格式中。
如果您眼尖,您可能已经注意到,由于结果对象字段与查询中的字段名称匹配,但不包含参数,因此您无法直接查询具有不同参数的相同字段。这就是您需要别名的原因 - 它们让您可以将字段的结果重命名为任何您想要的名称。
在上面的示例中,两个hero
字段会发生冲突,但由于我们可以将它们别名为不同的名称,因此我们可以在一个请求中获得两个结果。
假设我们的应用程序中有一个比较复杂的页面,它让我们可以并排查看两个英雄及其朋友。您可以想象这样的查询很快就会变得复杂,因为我们需要至少重复一次字段 - 每个比较的一侧。
这就是 GraphQL 包含可重用单元的原因,称为片段。片段让您可以构建字段集,然后在需要时将它们包含在查询中。以下是如何使用片段解决上述情况的示例
您可以看到,如果重复字段,上面的查询会非常重复。片段的概念经常用于将复杂的应用程序数据需求拆分成更小的块,尤其是在您需要将大量 UI 组件与不同的片段组合成一个初始数据获取时。
片段可以访问在查询或变异中声明的变量。请参阅 变量。
在上面的一些示例中,我们使用了一种简写语法,省略了query
关键字和查询名称,但在生产应用程序中,使用这些语法可以使我们的代码更清晰。
以下示例包含query
关键字作为操作类型,以及HeroNameAndFriends
作为操作名称
操作类型可以是query、mutation或subscription,它描述了你打算执行的操作类型。操作类型是必需的,除非你使用查询简写语法,在这种情况下,你不能为你的操作提供名称或变量定义。
操作名称是操作的有意义且明确的名称。它仅在多操作文档中是必需的,但鼓励使用它,因为它对于调试和服务器端日志记录非常有用。当出现问题时(你在网络日志或 GraphQL 服务器日志中看到错误),通过名称识别代码库中的查询比尝试破译内容更容易。把它想象成你最喜欢的编程语言中的函数名。例如,在 JavaScript 中,我们可以轻松地只使用匿名函数,但当我们给函数命名时,更容易跟踪它、调试代码以及在调用时记录它。同样,GraphQL 查询和变异名称以及片段名称可以作为服务器端识别不同 GraphQL 请求的有用调试工具。
到目前为止,我们一直在查询字符串中写入所有参数。但在大多数应用程序中,字段的参数将是动态的:例如,可能有一个下拉菜单让你选择你感兴趣的星球大战剧集,或者一个搜索字段,或者一组过滤器。
将这些动态参数直接传递到查询字符串中不是一个好主意,因为这样我们的客户端代码就需要在运行时动态地操作查询字符串,并将其序列化为 GraphQL 特定的格式。相反,GraphQL 有一种一流的方式将动态值从查询中分解出来,并将其作为单独的字典传递。这些值称为变量。
当我们开始使用变量时,我们需要做三件事
$variableName
替换查询中的静态值$variableName
声明为查询接受的变量之一variableName: value
以下是所有内容的示例
现在,在我们的客户端代码中,我们可以简单地传递一个不同的变量,而无需构建一个全新的查询。这通常也是一个很好的做法,用于表示查询中的哪些参数应该动态变化——我们不应该使用字符串插值从用户提供的 value 中构建查询。
变量定义是查询中类似于 ($episode: Episode)
的部分。它与类型化语言中函数的参数定义类似。它列出了所有变量,以 $
为前缀,后跟它们的类型,在本例中为 Episode
。
所有声明的变量必须是标量、枚举或输入对象类型。因此,如果要将复杂对象传递到字段中,则需要知道服务器上匹配的输入类型。在 Schema 页面上了解有关输入对象类型的更多信息。
变量定义可以是可选的或必需的。在上面的例子中,由于 Episode
类型旁边没有 !
,所以它是可选的。但是,如果要将变量传递到的字段需要非空参数,则变量也必须是必需的。
要了解有关这些变量定义语法的更多信息,学习 GraphQL 模式语言非常有用。模式语言在 Schema 页面上进行了详细说明。
通过在类型声明后添加默认值,也可以为查询中的变量分配默认值。
query HeroNameAndFriends($episode: Episode = JEDI) { hero(episode: $episode) { name friends { name } }}
当为所有变量提供默认值时,可以调用查询而不传递任何变量。如果作为变量字典的一部分传递了任何变量,它们将覆盖默认值。
我们上面讨论了变量如何使我们能够避免手动进行字符串插值来构建动态查询。在参数中传递变量解决了这类问题中很大一部分,但我们可能还需要一种方法来使用变量动态更改查询的结构和形状。例如,我们可以想象一个具有摘要视图和详细视图的 UI 组件,其中一个包含比另一个更多的字段。
让我们为这样的组件构建一个查询
尝试编辑上面的变量,改为为 withFriends
传递 true
,看看结果如何变化。
我们需要使用 GraphQL 中一个名为指令的新功能。指令可以附加到字段或片段包含,并且可以以服务器希望的任何方式影响查询的执行。核心 GraphQL 规范包含正好两个指令,任何符合规范的 GraphQL 服务器实现都必须支持它们。
@include(if: Boolean)
仅当参数为true
时才将此字段包含在结果中。@skip(if: Boolean)
如果参数为true
,则跳过此字段。指令对于摆脱需要进行字符串操作才能在查询中添加和删除字段的情况非常有用。服务器实现也可以通过定义全新的指令来添加实验性功能。
大多数关于 GraphQL 的讨论都集中在数据获取上,但任何完整的数据平台都需要一种方法来修改服务器端数据。
在 REST 中,任何请求最终都可能导致服务器上的一些副作用,但按照惯例,建议不要使用GET
请求来修改数据。GraphQL 类似 - 从技术上讲,任何查询都可以实现为导致数据写入。但是,建立一个约定很有用,即任何导致写入的操作都应该通过变异显式发送。
就像在查询中一样,如果变异字段返回一个对象类型,你可以请求嵌套字段。这对于在更新后获取对象的最新状态很有用。让我们看一个简单的变异示例
注意createReview
字段如何返回新创建的评论的stars
和commentary
字段。这在变异现有数据时尤其有用,例如,当递增字段时,因为我们可以使用一个请求来变异和查询字段的新值。
你可能还会注意到,在这个例子中,我们传入的review
变量不是一个标量。它是一个输入对象类型,一种可以作为参数传入的特殊对象类型。在“模式”页面上了解更多关于输入类型的知识。
变异可以包含多个字段,就像查询一样。除了名称之外,查询和变异之间有一个重要的区别。
虽然查询字段是并行执行的,但变异字段是按顺序运行的,一个接一个。
这意味着,如果我们在一个请求中发送两个incrementCredits
变异,第一个变异保证在第二个变异开始之前完成,确保我们不会与自己发生竞争条件。
与许多其他类型系统一样,GraphQL 模式包括定义接口和联合类型的能力。 在模式指南中了解它们。
如果你正在查询一个返回接口或联合类型的字段,你需要使用内联片段来访问底层具体类型上的数据。用一个例子来说明最容易理解。
在这个查询中,hero
字段返回类型 Character
,它可能是 Human
或 Droid
,具体取决于 episode
参数。在直接选择中,你只能请求 Character
接口上存在的字段,例如 name
。
要请求具体类型上的字段,你需要使用带有类型条件的内联片段。因为第一个片段被标记为 ... on Droid
,所以 primaryFunction
字段只有在从 hero
返回的 Character
是 Droid
类型时才会执行。类似地,Human
类型的 height
字段也是如此。
命名片段也可以以相同的方式使用,因为命名片段始终带有类型。
鉴于在某些情况下你不知道从 GraphQL 服务返回什么类型,你需要某种方法来确定如何在客户端处理这些数据。GraphQL 允许你在查询中的任何位置请求 __typename
(一个元字段),以获取该位置对象类型的名称。
在上面的查询中,search
返回一个联合类型,它可以是三种选项之一。如果没有 __typename
字段,客户端将无法区分不同的类型。
GraphQL 服务提供了一些元字段,其余的用于公开 Introspection 系统。