Graphql 支持的语法格式笔记

graphql 查询方式笔记

普通查询一个字段

例1

{
  hero {
    name
  }
}

返回值

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

例2

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

返回值

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

带参数查询

{
  human(id: "1000") {
    name
    height
  }
}
{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 1.72
    }
  }
}
{
  human(id: "1000") {
    name
    height(unit: FOOT)
  }
}
{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 5.6430448
    }
  }
}

使用别名来避免返回值中包含相同字段

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}
{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}

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 HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

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

操作名称是你的操作的有意义和明确的名称。它仅在有多个操作的文档中是必需的,但我们鼓励使用它,因为它对于调试和服务器端日志记录非常有用。 当在你的网络或是 GraphQL 服务器的日志中出现问题时,通过名称来从你的代码库中找到一个查询比尝试去破译内容更加容易。 就把它想成你喜欢的程序语言中的函数名。例如,在 JavaScript 中,我们只用匿名函数就可以工作,但是当我们给了函数名之后,就更加容易追踪、调试我们的代码,并在其被调用的时候做日志。同理,GraphQL 的查询和变更名称,以及片段名称,都可以成为服务端侧用来识别不同 GraphQL 请求的有效调试工具。

使用变量

使用变量之前,我们得做三件事:

  • 使用 $variableName 替代查询中的静态值。

  • 声明 $variableName 为查询接受的变量之一。

  • variableName: value 通过传输专用(通常是 JSON)的分离的变量字典中。

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

这样一来,我们的客户端代码就只需要传入不同的变量,而不用构建一个全新的查询了。这事实上也是一个良好实践,意味着查询的参数将是动态的 —— 我们决不能使用用户提供的值来字符串插值以构建查询

变量定义(Variable definitions)

变量定义看上去像是上述查询中的 ($episode: Episode)。其工作方式跟类型语言中函数的参数定义一样。它以列出所有变量,变量前缀必须为 $,后跟其类型,本例中为 Episode。

所有声明的变量都必须是标量、枚举型或者输入对象类型。所以如果想要传递一个复杂对象到一个字段上,你必须知道服务器上其匹配的类型。可以从Schema页面了解更多关于输入对象类型的信息。

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

如果想要进一步了解变量定义的句法,可以学习 GraphQL 的 schema 语言。schema 语言在 Schema 中有细述。

默认变量(Default variables)

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

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

尝试修改上面的变量,传递 true 给 withFriends,看看结果的变化。

我们用了 GraphQL 中一种称作指令的新特性。一个指令可以附着在字段或者片段包含的字段上,然后以任何服务端期待的方式来改变查询的执行。

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

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

  • @skip(if: Boolean) 如果参数为 true,跳过此字段。

指令在你不得不通过字符串操作来增减查询的字段时解救你。服务端实现也可以定义新的指令来添加新的特性。

Mutations

GraphQL 的大部分讨论集中在数据获取,但是任何完整的数据平台也都需要一个改变服务端数据的方法。

REST 中,任何请求都可能最后导致一些服务端副作用,但是约定上建议不要使用 GET 请求来修改数据。GraphQL 也是类似 —— 技术上而言,任何查询都可以被实现为导致数据写入。然而,建一个约定来规范任何导致写入的操作都应该显式通过变更(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 字段如何返回了新建的 review 的 stars 和 commentary 字段。这在变更已有数据时特别有用,例如,当一个字段自增的时候,我们可以在一个请求中变更并查询这个字段的新值。

你也可能注意到,这个例子中,我们传递的 review 变量并非标量。它是一个输入对象类型,一种特殊的对象类型,可以作为参数传递。你可以在 Schema 页面上了解到更多关于输入类型的信息。

变更中的多个字段(Multiple fields in mutations)

一个变更也能包含多个字段,一如查询。查询和变更之间名称之外的一个重要区别是:

查询字段时,是并行执行,而变更字段时,是线性执行,一个接着一个。

这意味着如果我们一个请求中发送了两个 incrementCredits 变更,第一个保证在第二个之前执行,以确保我们不会出现竞态。

内联片段(Inline Fragments)[这个地方和上面的片段一样, 还没太理解]

跟许多类型系统一样,GraphQL schema 也具备定义接口和联合类型的能力。在 schema 指南中可了解更多。

如果你查询的字段返回的是接口或者联合类型,那么你可能需要使用内联片段来取出下层具体类型的数据:

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)

某些情况下,你并不知道你将从 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 字段的情况下,几乎不可能在客户端分辨开这三个不同的类型。

GraphQL 服务提供了不少元字段,剩下的部分用于描述 内省 系统。

posted @ 2022-01-16 17:05  YanyuWu  阅读(228)  评论(0编辑  收藏  举报