标签:blank tco 副作用 rest服务 区分 ros asc operation arch
在这个网页,您将详细了解关于如何查询graphql服务器。
在其最简单的,GraphQL是需要特定的领域对象。让我们从一个非常简单的查询开始,以及在运行该查询时得到的结果:
{
hero {
name
}
}
结果:
{ "data": { "hero": { "name": "R2-D2" } } }
您可以立即看到右边和左边相同的结构,这就是graphql的查询,根据你自己定义的返回你所期望的字段。
这个字段返回一个字符串类型,是星球大战英雄的名字"R2-D2"。
上面的查询是交互式的。这意味着你可以随意改变它,看看新的结果。试着在查询对象中添加一个英雄出现在现场,看新的结果。、
在前面的示例中,我们只要求返回字符串的英雄的名称,但字段也可以引用对象。在这种情况下,可以对该对象进行字段的子选择。graphql查询可以遍历相关的对象和字段,让客户获取一个请求相关的数据。
{ hero { name # Queries can have comments! friends { name } } }
结果:
{ "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } }
注意,在这个示例中,friends字段返回一个数组项。一个对象和数组的graphql查询看起来是一样的,但是显示的时候是根据定义的类型来显示的。
参数
为graphql语言增加参数,只返回符合条件的数据,类似于数据库查询。
看个例子:
{ human(id: "1000") { name height } }
结果
{ "data": { "human": { "name": "Luke Skywalker", "height": 1.72 } } }
像REST,你只能传递一组参数——你的请求包括查询参数和URL段。但是GraphQL,每个自动和嵌套对象可以有自己的参数,一个graphql就可以替代多个api获取数据。您甚至可以将参数传递到标量字段,以便在服务器上实现数据转换,而不是单独地在每个客户端上实现数据转换。
{ human(id: "1000") { name height(unit: FOOT) } }
结果
{ "data": { "human": { "name": "Luke Skywalker", "height": 5.6430448 } } }
参数可以有许多不同的类型,在上面的例子中,我们使用了枚举类型,它表示一组有限的选择(在这种情况下,长度单位,无论是METER还是FOOT)。GraphQL有一组默认的类型,但graphql服务器也可以定义自己的自定义类型,只要可以序列化传输格式。
由于结果对象字段与查询中字段的名称匹配,但不包含参数,您不能直接用不同的参数对同一字段进行查询。这就是为什么你需要别名-它们让你把一个字段的结果重命名为你想要的任何东西。
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
结果
{ "data": { "empireHero": { "name": "Luke Skywalker" }, "jediHero": { "name": "R2-D2" } } }
在上面的例子中,两个英雄字段可能是冲突的,但是因为我们可以把它们别名不同的名字,我们可以在一个请求中得到两个结果。
假设我们的应用程序中有一个相对复杂的页面,让我们一起看两个英雄,和他们的朋友一起。可以想象这样的查询很快就会变得复杂,因为我们需要至少重复两个字段-一个针对比较的每个comparison, 这就是为什么GraphQL包括可重用的单元片段片段允许您构造字段集,然后将它们包含在您需要的查询中。下面是一个示例,说明如何使用片段解决上述情况:
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
结果
{ "data": { "leftComparison": { "name": "Luke Skywalker", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], "friends": [ { "name": "Han Solo" }, { "name": "Leia Organa" }, { "name": "C-3PO" }, { "name": "R2-D2" } ] }, "rightComparison": { "name": "R2-D2", "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } }
您可以看到,如果重复了字段,上面的查询将会有很多重复的数据。片段的概念经常用于将复杂的应用程序数据需求分割成较小的块,特别是当您需要将许多具有不同片段的ui组件组合到一个初始数据获取中时。
到目前为止,我们已经在查询字符串中编写了所有的参数。但是在大多数应用程序中,字段的参数都是动态的:例如,可能有一个下拉列表可以让您选择您感兴趣的“星战”集,或者搜索字段,或一组筛选器。
将这些动态参数直接传递到查询字符串中是不太好的做法,因为之后我们的客户端代码将需要在运行时动态地操作查询字符串,并将其序列化为特定于GraphQL-specific的格式。相反,GraphQL有一种方法 first-class 来将动态值从查询中提取出来,并将它们作为单独的字典传递。这些值称为variables。
当我们开始处理变量时,我们需要做三件事:
value
在单独的,传输特定(通常是JSON)变量字典中。这里的一切看起来都是这样的:
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
参数
{ "episode": "JEDI" }
结果
{ "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } }
现在,在我们的客户端代码中,我们可以简单地传递一个不同的变量,而不需要构造一个全新的查询。这也是一个很好的实践,用于指示我们的查询中的哪些参数是动态的-我们永远不应该使用字符串插值来构造来自用户提供的值的查询。
变量定义是看起来像 ($episode: Episode)
在上面的查询中。 它与类型化语言中函数的参数定义一样。它列出所有变量,前缀 $
,
然后是它们的类型,在本例中是Episode。
所有声明的变量必须是标量、枚举或输入对象类型。因此,如果要将复杂对象传递到字段,您需要知道在服务器上匹配的输入类型。
变量定义可以是可选的,也可以是必需的。在上面的例子中,因为没有一个!
旁边的Episode类型,它是可选的。但是,如果将变量传递到字段中需要一个非空参数,那么该变量也必须是必需的。
要了解这些变量定义的语法,需要学习graphql的schema语法。
还可以通过在类型声明之后添加默认值,将默认值分配给查询中的变量。
query HeroNameAndFriends($episode: Episode = "JEDI") {
hero(episode: $episode) {
name
friends {
name
}
}
}
当为所有变量提供默认值时,您可以调用查询而不传递任何变量。如果任何变量作为变量字典的一部分传递,它们将覆盖默认值。
在上面的示例中我们看到的一件事是,我们的查询获得了operation name。到目前为止,我们一直在使用一种速记语法,在这里我们省略了query关键字和查询名称,但是在生产应用程序中使用这些代码使我们的代码不那么模棱两可很有用。
在您最喜欢的编程语言中,就像一个函数名一样。例如,在JavaScript中,我们只需使用匿名函数就可以轻松地工作,但是当我们给一个函数一个名称时,它就更容易跟踪它,调试我们的代码,并在调用它时进行日志记录。同样,GraphQL查询和变异名称以及片段名称可以是服务器端的一个有用的调试工具,用于识别不同的GraphQL请求。
指令
我们在上面讨论了变量如何使我们避免手工字符串插值来构造动态查询。在参数中传递变量可以解决相当大的一类问题,但我们可能还需要一种方法,通过变量动态地改变查询的结构和形状。例如,我们可以想象一个具有总结和详细视图的用户界面组件,其中一个包含了比另一个字段更多的字段。
让我们为这样一个组件构造一个查询:
query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { name } } }
参数
{ "episode": "JEDI", "withFriends": false }
结果
{ "data": { "hero": { "name": "R2-D2" } } }
试着编辑上面的变量来代替true
为withfriends
,并查看结果如何变化。
我们需要在GraphQL中使用一个新特性,称为directive指令可以附加到字段或片段包含,并且可以以服务器希望的任何方式影响查询的执行。核心GraphQL规范完全包含两个指令,这两个指令必须由任何符合规范的GraphQL服务器实现支持:
指令对于避免在查询中添加和移除字段时需要进行字符串操作的情况很有用。服务器实现还可以通过定义全新的指令来添加实验特性。
大部分讨论都集中在数据获取上,但任何完整的数据平台都需要一种修改服务器端数据的方法。
在REST服务,任何请求可能最终会在服务器上造成一些副作用,但按照惯例,建议不使用get
请求修改数据。GraphQL从技术上讲,任何查询都可以实现,从而导致数据写入。但是,建立一个约定,使得任何导致写入的操作都应该通过一个mutation显式发送,这是很有用的。
就像在查询中一样,如果在字段返回一个对象类型,您可以请求嵌套字段。这对于在更新后获取对象的新状态很有用。让我们看一个简单的示例mutation:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
参数
{ "ep": "JEDI", "review": { "stars": 5, "commentary": "This is a great movie!" } }
结果
{ "data": { "createReview": { "stars": 5, "commentary": "This is a great movie!" } } }
注意如何createReview
字段返回stars
和commentary新创建的评论的字段。这在变异现有数据时特别有用,例如,在递增一个字段时,因为我们可以用一个请求进行变异并查询字段的新值。
您可能还注意到,在这个示例中,审查
传入的变量不是标量。这是个输入对象类型一种特殊类型的对象类型,可以作为参数传入。了解有关模式页面上输入类型的更多信息。请参考schema语法。
一个mutations可以包含多个字段,就像一个查询一样。除了名称之外,查询和突变之间有一个重要的区别:
当查询字段并行执行时,mutations字段会连续运行,一个接一个地运行。
这意味着如果我们在一个请求中发送两个incrementCredits mutations,第一个是保证在第二个开始之前完成,确保都执行成功。
像许多其他类型的系统一样,GraphQL模式包括定义接口和联合类型的能力。
如果查询返回接口或联合类型的字段,您需要使用内联片段来访问底层具体类型的数据。用一个例子来看是最容易的:
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
参数
{ "ep": "JEDI" }
结果
{ "data": { "hero": { "name": "R2-D2", "primaryFunction": "Astromech" } } }
在这个查询中,在hero
字段返回类型Character,这可能是一个Human
或一个Droid,取决于episode
参数。在直接选择中,您只能请求在Character
接口上存在的字段,例如name。
若要在具体类型上请求字段,需要使用带有类型条件的内联片段。
命名的片段也可以以同样的方式使用,因为一个命名的片段总是有一个附加的类型。
考虑到有些情况下您不知道从在服务中得到什么类型,您需要一些方法来确定如何处理客户端上的数据。GraphQL允许您在查询中的任何位置请求_typeName,一个元字段,以便在该点获取对象类型的名称。
{ search(text: "an") { __typename ... on Human { name } ... on Droid { name } ... on Starship { name } } }
结果
{ "data": { "search": [ { "__typename": "Human", "name": "Han Solo" }, { "__typename": "Human", "name": "Leia Organa" }, { "__typename": "Starship", "name": "TIE Advanced x1" } ] } }
在这个查询中,search返回一个联合类型,它可以是三个选项之一。如果没有_typeName字段,就不可能区分不同类型的客户端。
GraphQL服务提供了一些元字段,其余的请查看Introspection 目录
标签:blank tco 副作用 rest服务 区分 ros asc operation arch
原文地址:http://www.cnblogs.com/mcbeath/p/7817019.html