介绍怎么查询 GraphQL 服务。
字段:Fields
最简单的查询就是告诉 Graphql 你想要的字段是什么:
{ hero { name } }
查询结果:
{ "data": { "hero": { "name": "R2-D2" } } }
注意看查询与结果的数据形状,它们是一致的。这是 GraphQL 最重要的一个特点,你会获取到期待的数据,服务器确切地知道客户端想要的字段。
name 字段的类型是 String,上面例子里,name 的值是 R2-D2,他是电影 Star Wars 里的人物。
上面这个例子,你要的是人物的 name,返回的是个字段串类型的数据。字段的值也可以是对象,你可以选择对象里的某些字段。GraphQL 的查询可以找到相关的对象还有它们的字段,这样客户端只需要用一个请求就可以提取大量相关的数据。如果是 REST,可能需要来回跑几次才能得到需要的数据。
查询:
{ 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 查询,看起来都差不多。字段的类型在 Schema 里面有相关的标注。
参数:Arguments
给字段添加参数。
查询:
{ human(id: "1000") { name height } }
结果:
{ "data": { "human": { "name": "Luke Skywalker", "height": 1.72 } } }
在 REST 里面,你只能通过地址片断还有查询参数来传递参数。在 GraphQL 里面,每个字段,嵌套的对象都可以得到它自己的参数。你甚至可以给 Scalar 字段传递参数,可以在服务端就实现数据转换,不用单独在每个客户端去做这件事。
查询:
{ human(id: "1000") { name height(unit: FOOT) } }
结果:
{ "data": { "human": { "name": "Luke Skywalker", "height": 5.6430448 } } }
参数可以是很多不同的类型。上面的例子用的是 Enumeration 类型,表示的是一组可选的选项(这里就是长度单位,可以是 METER 或 FOOT)。GraphQL 里面带着一些默认的类型,你也可以声明自定义的类型。
别名:Aliases
使用别名,可以重命名结果里的字段的名字。
查询:
{ empireHero: hero(episode: EMPIRE) { name } jediHero: hero(episode: JEDI) { name } }
结果:
{ "data": { "empireHero": { "name": "Luke Skywalker" }, "jediHero": { "name": "R2-D2" } } }
在上面,两个 hero 字段有冲突,但我们可以分别给它们起个别名,这样可以用一个请求就能同时得到这两个结果。
片断:Fragments
比如我们的应用里有个复杂的页面,可以让我们对比两个人物还有他们的朋友们。这里可以用 GraphQL 里的 fragments 创建可以重复使用的单元,fragments 的目的就是把应用里的经常用的复杂的数据需求分割成小块。
查询:
{ 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" } ] } } }
操作名:Operation name
之前我们在查询里忽略掉了 query 还有查询名,这是一种简单的写法。不过我们最好加上这些东西,可以让应用的代码更清晰一些。在执行 query 以外的操作,或者传递动态变量的时候,在查询里必须得加上 GraphQL 操作。
下面这个例子用了 query 作为操作类型,HeroNameAndFriends 作为操作名:
查询:
query HeroNameAndFriends { hero { name friends { name } } }
结果:
{ "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } }
操作类型可以是 query,mutation,或者 subscription 还有 describes。操作名可以用来描述查询操作,可以把它想成是个函数名,在调试应用的时候,操作名可以让我们更清楚问题出在了哪里。
变量:Variables
查询可能是动态的,比如根据下拉菜单的选择,搜索,或者一些过滤器的值动态执行查询。GraphQL 提供了一种方法,可以把查询中的动态值从查询里单独拿出来,放到一个独立的 dictionary 里面,这些动态值在 GraphQL 里叫变量(variables)。
使用变量,要做三件事:
- 用 $variableName 替换查询中的静态值。
- 声明 $variableName 是查询中可以接受的一个变量。
- 用独立的变量字典传递 variableName: value 。
查询:
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 就是变量的定义。它有点像是 typed 程序语言里的函数里面定义的参数。列出所有变量,用 $ 作用前缀,后面跟着它们的类型,上面的例子变量的类型就是 Episode。
声明的变量必须是 scalars,enums,或者 input object 类型。所以你想给字段传递一个复杂的对象,你需要知道哪种 input 类型跟服务端匹配。
变量定义可以是可选的,也可以是必须的。上面的例子里,在 Episode 后面没有 ! 号,所以它是可选的。不过如果你要传递变量的那个字段需要一个 non-null 参数,那么变量也会是必须的。
默认变量
在 type 声明的后面可以添加参数的默认值。
query HeroNameAndFriends($episode: Episode = "JEDI") { hero(episode: $episode) { name friends { name } } }
指令:Directives
使用指令,配合变量,可以动态地改变查询的结构。假设一个界面组件,有一个摘要视图跟详情视图,为这个组件构建一个查询:
query Hero($episode: Episode, $withFriends: Boolean!) { hero(episode: $episode) { name friends @include(if: $withFriends) { name } } }
变量:
{ "episode": "JEDI", "withFriends": false }
数据:
{ "data": { "hero": { "name": "R2-D2" } } }
如果把变量中的 withFriends 的值设置成 true,那返回的查询结果应该是:
{ "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } }
GraphQL 有两个指令:
- @include(if: Boolean),如果参数的值是 true 才会在查询结果中包含字段。
- @skip(if: Boolean),参数的值如果是 true,忽略这个字段。
修改:Mutaions
修改数据用 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!" } } }
修改中的多个字段
跟查询一样,在一个修改中也可以包含多个字段,不过一个重要的区别是,查询字段并行执行,修改字段是一个接一个地被执行。
Inline Fragments
查询:
query HeroForEpisode($ep: Episode!) { hero(episode: $ep) { name ... on Droid { primaryFunction } ... on Human { height } } }
变量:
{ "ep": "JEDI" }
结果:
{ "data": { "hero": { "name": "R2-D2", "primaryFunction": "Astromech" } } }
Meta fields
查询:
{ 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" } ] } }GraphQL