🦄 2024 独立开发者训练营,一起创业!查看介绍 / 立即报名 →

GraphQL:查询与修改(Queries and Mutations)

介绍怎么查询 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)。

使用变量,要做三件事:

  1. 用 $variableName 替换查询中的静态值。
  2. 声明 $variableName 是查询中可以接受的一个变量。
  3. 用独立的变量字典传递 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 有两个指令:

  1. @include(if: Boolean),如果参数的值是 true 才会在查询结果中包含字段。
  2. @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
微信好友

用微信扫描二维码,
加我好友。

微信公众号

用微信扫描二维码,
订阅宁皓网公众号。

240746680

用 QQ 扫描二维码,
加入宁皓网 QQ 群。

统计

14696
分钟
0
你学会了
0%
完成

社会化网络

关于

微信订阅号

扫描微信二维码关注宁皓网,每天进步一点