GraphQL学习笔记(3)入门、查询与变更

一、入门

GraphQL是一种用于API的查询语言,是基于类型系统来进行查询的。

一个GraphQL服务是通过定义类型和类型上的字段来创建的,然后给每个类型的每个字段提供解析函数。

定义类型示例:

type Query {
  me: User
}

type User {
  id: ID
  name: String
}

  定义解析函数示例:

function Query_me(request) {
  return request.auth.user;
}

function User_name(user) {
  return user.getName();
}

  一旦一个GraphQL服务端运行起来了,通常在web服务的一个URL上,这时它便可以接受GraphQL查询请求,并验证和执行。接收到的查询首先会被检查确保它只引用了已定义的类型和字段,然后运行指定的解析函数来生成结果。

客户端查询示例:

{
  me {
    name
  }
}

  服务器会返回的JSON效果

{
  "me": {
    "name": "Luke Skywalker"
  }
}

  

二、查询和变更数据

1、字段fields

GraphQL 是请求对象上的特定字段。然后服务器会返回请求字段相同结构的数据

示例:请求

{
  hero {
    name
  }
}

 结果示例:

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

  返回一个data的结构与请求的结构完全一致。

还可以对这个对象的字段进行次级选择(sub-selection)。GraphQL 查询能够遍历相关对象及其字段,使得客户端可以一次请求查询大量相关数据

次级选择请求示例:

{
  hero {
    name
    # 查询可以有备注!
    friends {
      name
    }
  }
}

 次级选择请求结果:

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },

  注意这个例子中,friends 返回了一个数组的项目,GraphQL 查询会同等看待单个项目或者一个列表的项目,然而我们可以通过 schema 所指示的内容来预测将会得到哪一种。 

 2、参数

每一个字段和嵌套对象都能有自己的一组参数

带参数的查询示例:

{
  human(id: "1000") {
    name
    height(unit: FOOT)
  }
}

  

带参数查询结果示例:

{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 5.6430448
    }
  }
}

  3、别名

由于结果字段中并不包含参数,因此如果你的查询请求仅仅是参数不同,那么查询便会报错,因为他们的查询结果的结构是一样的。这就导致在data中数据无法显示。因此,我们需要给相同结构的查询不同的名称,以便于在查询结构中显示不同名称。示例

带别名的相同结构的查询:

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

  带别名的相同结构的查询结果

{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}

  上例中,两个 hero 字段将会存在冲突,但是因为我们可以将其另取一个别名,我们也就可以在一次请求中得到两个结果。

4、片段(fragment)

如果我们需要在不同的地方引用相同的查询,我们可以使用片段(fragment)

相同查询复用示例:

{
  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"
        }
      ]
    }
  }
}

  5、操作名称

这之前,我们都使用了简写句法,省略了 query 关键字和查询名称,但是生产中使用这些可以使我们代码减少歧义。

操作类型可以是 querymutation 或 subscription,描述你打算做什么类型的操作。操作类型是必需的,除非你使用查询简写语法,在这种情况下,你无法为操作提供名称或变量定义。

操作名称是你的操作的有意义和明确的名称。它仅在有多个操作的文档中是必需的,但我们鼓励使用它,因为它对于调试和服务器端日志记录非常有用。

操作名称查询示例:

query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}

  查询结果示例:

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

  6、变量

如果需要动态的向参数传递查询值,如下拉菜单选择后自动查询,这时就会用到变量,使用变量的步骤:

(1)使用$变量名代替原来的静态变量

(2)变量定义。$变量名:类型。在操作名称上面声明$变量名:类型为查询接受的变量之一。

(3)将 variableName: value传递进查询中

示例:

# { "graphiql": true, "variables": { "episode": JEDI } }
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

  变量定义:变量定义可以是可选的或者必要的。上例中,Episode 后并没有 !,因此其是可选的。但是如果你传递变量的字段要求非空参数,那变量一定是必要的。

默认变量:

可以通过在查询中的类型定义后面附带默认值的方式,将默认值赋给变量。

 示例:

query HeroNameAndFriends($episode: Episode = "JEDI") {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

 7、指令

如果我们希望能够动态的改变查询的结构,我们可以使用指令。

指令示例如下:

query Hero($episode: Episode, $withFriends: Boolean!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}

  GraphQL 的核心规范包含两个指令,其必须被任何规范兼容的 GraphQL 服务器实现所支持:

  • @include(if: Boolean) 仅在参数为 true 时,包含此字段。
  • @skip(if: Boolean) 如果参数为 true,跳过此字段。

参数:

{
  "episode": "JEDI",
  "withFriends": false
}

  查询结果:

{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

  8、变更(mutations)

修改数据示例:

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!"
    }
  }
}

  GraphQL可以在提交修改的时候同时提交查询,获得修改后的结果。当一个字段自增的时候,我们可以在一个请求中变更并查询这个字段的新值。

查询和变更多个字段的区别:

查询字段时,是并行执行,而变更字段时,是线性执行,一个接着一个。这意味着如果我们一个请求中发送了两个 incrementCredits 变更,第一个保证在第二个之前执行,以确保我们不会出现竞态。

9、内联片段

跟许多类型系统一样,GraphQL schema 也具备定义接口和联合类型的能力。如果你查询的字段返回的是接口或者联合类型,那么你可能需要使用内联片段来取出下层具体类型的数据

示例:查询

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
    ... on Human {
      height
    }
  }
}

  参数:

{
  "ep": "JEDI"
}

  结果:

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}

  10、元字段

某些情况下,你并不知道你将从 GraphQL 服务获得什么类型,这时候你就需要一些方法在客户端来决定如何处理这些数据。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字段的情况下,几乎不可能在客户端分辨开这三个不同的类型。

 

posted @ 2018-08-25 11:29  tutu_python  阅读(591)  评论(0)    收藏  举报