GraphQL 的 Queries 和 Mutations
官方文档链接:Queries and Mutations
本文概述的都是最基础的查询语句,具体的建立连接和发送请求的方式可以查看 Apollo 相关文档
Fields (字段)
在最简单的情况下,GraphQL 可以查询对象上的指定字段,看下下面这个简单的查询案例
query {
hero {
name
}
}
### REULT ###
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
你可以很清楚地看见,返回的结果和查询的结构是一致的。
在上面的案例中,就简单获取了 hero 的 name,但是某个查询的字段也可能是对象。下面这个例子就是字段嵌套子对象的情况。
query {
hero {
name
# Queries can have comments!
friends {
name
}
}
}
### RESULT ###
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
GraphQL 的查询语句会遍历字段中所有相近的对象,尽量让客户端在一个请求中获取大量的相关数据,而不是多次请求。
Arguments (参数)
在实际的请求过程中,我们都需要请求符合某个条件的指定数据,所以在 GraphQL 中我们也可以将参数传递给字段,就像下面这样:
query {
human(id: "1000") {
name
height
}
}
### RESULT ###
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
Operation Name (操作名称)
上面的例子都是直接使用 query 这个关键字作为查询名称的(官方文档省略了前面的query)但是实际的开发过程中,肯定不希望代码描述太过模糊,所以可以给操作设定名称
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
不光是 query 操作,后续的 mutation 和 subscription 都可以指定操作名称用于区分不同的条件语句。
Variables (变量)
光能够指定明确的参数值肯定是不符合开发要求的,很多时候我们一个 query 语句肯定要给不同的参数值去查询的,这个时候就可以使用变量进行传参:
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
--- variables ---
{
"episode": "JEDI"
}
### RESULT ###
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
简单概括一下上面的例子:
- 使用 $variableName 代替静态的查询值
- 将 $variableName 作为查询的形式参数
- 传递实参进入 $variableName
另外和 JS 的函数传参一样,也可以给个默认的变量值
query HeroNameAndFriends($episode: Episode = JEDI) {
hero(episode: $episode) {
name
friends {
name
}
}
}
Aliases (别名)
上面所有例子的情况,都有一个默认的前提,那就是服务器端的字段和前端所需的字段都是吻合的。那如果要是前端所需的字段需要换个别名再去渲染那该怎么办呢?下面的例子就能满足这样的需求:
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
### RESULT ###
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
上面的例子就是,两个 hero 字段被重新换名了然后再返回给客户端。
Fragments (片段)
继续上面使用别名的场景,如果客户端给同一数据类型定义了两个不同的别名,然后又恰好不幸的是,查询的数据结构又很复杂,但是我们又不想再写一遍完全一样的数据结构怎么办?下面这个例子就能解决这种问题:
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
### RESULT ###
{
"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"
}
]
}
}
}
你可以看到上面就是客户端需要查询两派不同的 hero 但是它们的属性基本相同,这个时候我们肯定不希望重复地去写两次类型声明,这个时候我们就可以预设好一个 Fragments 去复用。另外如果你要在 Fragments 里使用变量也是可以的:
query HeroComparison($first: Int = 3) {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
friendsConnection(first: $first) {
totalCount
edges {
node {
name
}
}
}
}
Directives (指令)
上面的变量的例子,已经能帮助我们动态地获取想要的后端数据了,但有时候我们还可能需要根据变量来动态地去更改我们的结构。例如,有时候渲染某个 UI 组件需要指定的条件,不符合这个条件我们就不渲染这个 UI 组件。下面这个例子就展示了如何使用 directives 关键字去动态地更改我们需要查询的数据结构:
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
--- variables ---
{
"episode": "JEDI",
"withFriends": false
}
### RESULT ###
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
简单解释一下上面这个例子,当 withFriends 的值为 false 时,该 hero 结构就不需要对应的 friends 属性了。
Mutations (变更)
刚刚所有的讨论都是围绕着请求数据,但我们肯定也需要一种能够修改服务器端数据的方法。在 REST 风格的请求方式下,尽管 GET 也能修改服务端数据,但通常情况下我们还是使用 POST 去修改服务端数据。GraphQL 也是类似的逻辑,虽然 query 也可以实现数据的写入,但任何的写入操作都应该用 mutation 关键字。
就像 query 一样,mutation 操作也可以返回指定的对象类型。它可以返回更新后的最新状态。样例如下:
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
--- variables ---
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
### RESULT ###
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
这样我们在更新一个字段的时候,我们就可以在一次请求中更新并获取最新的值。
另外需要注意的是:Query 操作是并行的,而 Mutation 操作是串行的,这就意味着如果你在一次 request 中发送两次 CreateReviewForEpisode
,那么第一次一定会在第二次开始之前结束,用以确保不阻塞自己。
另外,如果你不能确定从服务端返回的是哪种类型的话,你还可以加上 __typename
这个字段 (两条下划线)。