GraphQL 规范(中文版)
    GraphQL 规范(中文版)
    • GraphQL 规范 (中文版)

    GraphQL 规范 (中文版)

    1、概述#

    GraphQL 是一种查询语言,旨在通过提供直观灵活的语法和系统来描述客户端应用程序的数据需求和交互,从而构建客户端应用程序。
    例如,此 GraphQL 请求将从 GraphQL 的 Facebook 实现接收 id 为 4 的用户的名称。
    例子3{
      user(id: 4) {
        name
      }
    }
    生成结果数据(JSON 格式):
    例子4{
      "user": {
        "name": "Mark Zuckerberg"
      }
    }
    GraphQL 不是一种能够进行任意计算的编程语言,而是一种用于向具有本规范中定义的功能的应用程序服务发出请求的语言。GraphQL 并不要求实现它的应用程序服务使用特定的编程语言或存储系统。相反,应用程序服务利用它们的功能并将它们映射到 GraphQL 编码的统一语言、类型系统和哲学。这为产品开发提供了一个统一的界面,并为工具构建提供了一个强大的平台。
    GraphQL 有许多设计原则:
    以产品为中心:GraphQL 毫无疑问是由视图和编写视图的前端工程师的需求驱动的。GraphQL 从他们的思维方式和需求开始,构建实现这一点所需的语言和运行时。
    层次结构:当今大多数产品开发都涉及视图层次结构的创建和操作。为了与这些应用程序的结构保持一致,GraphQL 请求本身是分层结构的。请求的形状就像其响应中的数据一样。这是客户描述数据需求的自然方式。
    强类型:每个 GraphQL 服务都定义了一个特定于应用程序的类型系统。请求在该类型系统的上下文中执行。给定 GraphQL 操作,工具可以在执行之前(即在开发时)确保该操作在该类型系统中语法正确且有效,并且服务可以对响应的形状和性质做出某些保证。
    客户端指定的响应:通过其类型系统,GraphQL 服务发布允许其客户端使用的功能。客户端负责准确指定如何使用这些已发布的功能。这些请求是在字段级粒度上指定的。在大多数不使用 GraphQL 编写的客户端-服务器应用程序中,服务确定从其各个端点返回的数据的形状。另一方面,GraphQL 响应仅包含客户端所请求的内容,没有多余的内容。
    内省:GraphQL 是内省的。GraphQL 服务的类型系统可以由 GraphQL 语言本身进行查询,如本规范中所述。GraphQL 内省为构建常见工具和客户端软件库提供了强大的平台。
    由于这些原则,GraphQL 是构建客户端应用程序的强大且高效的环境。产品开发人员和设计人员根据可用的 GraphQL 服务构建应用程序(在高质量工具的支持下)可以快速提高工作效率,而无需阅读大量文档,也无需接受正式培训。为了实现这种体验,必须有人构建这些服务和工具。
    以下正式规范可供那些建设者参考。它描述了语言及其语法、用于查询它的类型系统和内省系统,以及执行和验证引擎以及为其提供支持的算法。该规范的目标是为尚未构建的 GraphQL 工具、客户端库和服务实现(跨越组织和平台)的生态系统提供基础和框架。我们期待与社区合作以实现这一目标。

    2、语言#

    客户端使用 GraphQL 查询语言向 GraphQL 服务发出请求。我们将这些请求来源称为文档。文档可能包含操作(查询、突变和订阅)以及片段,这是一种常见的组合单元,允许对数据需求进行重用。
    GraphQL 文档被定义为一种语法规则,其中终结符是令牌(不可分割的词法单元)。这些令牌在词法规则中被定义为匹配源字符模式的词法单元。在该文档中,语法规则的产生式用冒号 : 表示,而词法规则的产生式用双冒号 :: 表示。
    GraphQL 文档的源文本必须是 SourceCharacter 序列。字符序列必须由一系列 Token 和 Ignored 词法语法来描述。词汇标记序列(省略 Ignored)必须由单个文档句法语法来描述。
    注意:有关本文档中使用的词汇和句法以及其他符号约定的更多信息,请 参阅附录 A。
    词法分析和句法分析
    GraphQL文档的源文本首先被转换为一系列词法标记(Token)和被忽略的标记(Ignored)。源文本从左到右进行扫描,重复地将符合词法语法产生式所允许的下一个可能的码点序列作为下一个标记。然后,这个词法标记序列从左到右进行扫描,根据文档的句法语法生成一个抽象语法树(AST)。
    本文档中的词法语法产生式使用先行限制来消除歧义并确保单个有效的词法分析。词汇标记仅在其前瞻限制中后面没有字符时才有效。
    例如,IntValue 具有 Digit 限制,因此后面不能跟 Digit 。因此,序列123不能表示令牌 ( 12 , 3 ),因为12后面跟着 Digit 3,因此必须仅表示单个令牌。在字符之间使用 WhiteSpace 或其他 Ignored 字符来表示多个标记。
    注意:通常情况下,它的行为与“最大匹配”(即尽可能长的匹配)相同,但是一些前瞻限制包括额外的约束条件。

    2.1 源文本#

    SourceCharacter :
    U+0009
    U+000A
    U+000D
    U+0020–U+FFFF
    GraphQL 文档是由一系列 Unicode 代码点(在本规范的大部分内容中非正式地称为“字符”)表示的。然而,除了少数例外情况外,大部分 GraphQL 只在原始的非控制 ASCII 范围内表示,以便与尽可能多的现有工具、语言和序列化格式兼容,并避免在文本编辑器和源代码控制中出现显示问题。
    注意:GraphQL 的 StringValue 和 Comment 部分可以自由地包含非 ASCII Unicode 字符。

    2.1.1 统一码#

    UnicodeBOM:
    字节顺序标记 (U+FEFF)
    “字节顺序标记”是一个特殊的 Unicode 字符,它可能出现在包含 Unicode 的文件的开头,程序可以使用它来确定文本流是 Unicode、文本流采用的字节顺序以及几种 Unicode 中的哪一种。要解释的编码。

    2.1.2 空格#

    White Space:
    Horizontal Tab (U+0009) (U+0009)
    Space (U+0020) (U+0020)
    空格用于提高源文本的易读性并充当标记之间的分隔,并且任意数量的空格可以出现在任何标记之前或之后。标记之间的空格对于 GraphQL 文档的语义意义并不重要,但是空格字符可能会出现在字符串或注释标记中。
    注意:GraphQL 故意不将 Unicode 的 “Zs” 类别字符视为空格,以避免被文本编辑器和源代码控制工具误解。

    2.1.3 行终止符#

    LineTerminator:
    New Line (U+000A)
    Carriage Return (U+000D)New Line (U+000A)
    Carriage Return (U+000D)New Line (U+000A)
    与空格类似,行终止符用于提高源代码的可读性和分隔词法标记。它们可以出现在任何其他标记之前或之后,并且对 GraphQL 文档的语义含义没有影响。行终止符不会出现在任何其他标记内部。
    注意:任何提供源代码中有错误语法的行号的错误报告都应使用前面的换行符数量来生成行号。

    2.1.4 评论#

    Comments:
    #CommentCharlist,optCommentChar
    CommentChar :
    SourceCharacter but not LineTerminator
    GraphQL源文件可以包含以 # 标记开头的单行注释。
    注释可以包含 SourceCharacter 中除 LineTerminator 之外的任何 Unicode 代码点,因此注释始终包含以 # 字符开头直到但不包括 LineTerminator (或源代码结尾)的所有代码点。
    注释像空格一样被忽略,可能出现在任何标记之后或 LineTerminator 之前,并且对 GraphQL 文档的语义没有意义。

    2.1.5 无关紧要的逗号#

    Commas
    ,
    与空格和行终止符类似,逗号 ( , ) 用于提高源文本的易读性和分隔词汇标记,但在 GraphQL 文档中在语法和语义上并不重要。
    不重要的逗号字符确保逗号的缺失或存在不会有意义地改变文档的解释语法,因为这可能是其他语言中常见的用户错误。它还允许在风格上使用尾随逗号或行终止符作为列表分隔符,这通常都是源代码的易读性和可维护性所需要的。

    2.1.6 词汇标记#

    Token :
    Punctuator 
    Name 
    IntValue 
    FloatValue 
    StringValue StringValue
    GraphQL 文档由几种不可分割的词汇标记组成,这些词汇标记在词汇语法中通过源 Unicode 字符模式定义。词汇标记可以用忽略标记分隔。
    令牌后来被用作 GraphQL 语法规则中的终端符号。

    2.1.7 忽略的令牌#

    Ignored :
    UnicodeBOM UnicodeBOM
    WhiteSpace 
    LineTerminator 
    Comment 
    Comma 
    忽略的令牌用于提高可读性并提供词汇标记之间的分隔,但在其他方面是无关紧要的,并且不会在句法语法产生式中引用。
    任何数量的 Ignored 可以出现在每个词法标记之前和之后。源文档中的任何被忽略的区域都不重要,但是在 Ignored 中出现的 SourceCharacter 也可以以重要的方式出现在词法标记中,例如 StringValue 可能包含空格字符。在词法标记中不允许出现 Ignored,例如在定义 FloatValue 的字符之间不允许出现空格字符。

    2.1.8 标点符号#

    标点符号:
    ! $ & ( ) ... : = @ [ ] { | }
    GraphQL 文档包含标点符号以描述结构。GraphQL 是一种数据描述语言,而不是一种编程语言,因此 GraphQL 缺少通常用于描述数学表达式的标点符号。

    2.1.9 名称#

    Name :
    NameStartNameContinue list,opt NameContinue
    NameStart :
    Letter 
    _
    NameContinue :
    Letter 
    Digit 
    _
    Letter :
    ABCDEFGHIJKLM
    NOPQRSTUVWXYZ
    abcdefghijklm
    nopqrstuvwxyz
    Digit :
    0123456789
    GraphQL 文档充满了命名的东西:操作、字段、参数、类型、指令、片段和变量。所有名称必须遵循相同的语法形式。
    GraphQL 中的名称区分大小写。也就是说name,Name 和 NAME都指代不同的名称。下划线很重要,它表示other_name和othername是两个不同的名称。
    Name 后面不能跟 NameContinue。换句话说,名称令牌始终是最长的可能有效序列。源字符a1不能解释为两个标记,因为 a 后面跟着NameContinue 1。
    注意:GraphQL中的名称仅限于拉丁ASCII字符子集,以便与尽可能多的其他系统进行互操作。
    保留名称
    GraphQL 类型系统中的任何名称都不能以两个下划线“__”开头,除非它是本规范定义的内省系统的一部分。
    分。

    2.2文档#

    Document:
    Definitionlist
    Definition:
    ExecutableDefinition
    TypeSystemDefinitionOrExtension
    ExecutableDocument:
    ExecutableDefinitionlist
    ExecutableDefinition:
    OperationDefinition
    FragmentDefinition
    GraphQL 文档描述了由 GraphQL 服务或客户端操作的完整文件或请求字符串。一个文档包含多个定义,可以是可执行的,也可以代表 GraphQL 类型系统。
    如果文档是 ExecutableDocument 并且至少包含一个 OperationDefinition ,则它们只能由 GraphQL 服务执行。包含 TypeSystemDefinitionOrExtension 的 Document不得被执行;接收包含这些内容的文档的 GraphQL 执行服务应返回描述性错误。
    只寻求执行 GraphQL 请求而不构建新的 GraphQL 模式的 GraphQL 服务,可以选择仅允许可执行文档。
    不包含 OperationDefinition 或包含 TypeSystemDefinitionOrExtension 的文档仍可能被解析和验证,以允许客户端工具表示可能出现在许多单独文件中的许多 GraphQL 使用。
    如果文档仅包含一个操作,则该操作可能是未命名的。如果该操作是没有变量或指令的查询,那么它也可以以速记形式表示,省略查询关键字和操作名称。否则,如果 GraphQL 文档包含多个操作,则必须命名每个操作。当向 GraphQL 服务提交包含多个操作的文档时,还必须提供要执行的所需操作的名称。

    2.3 运营#

    OperationDefinition:
    OperationTypeNameoptVariableDefinitionsoptDirectivesoptSelectionSet
    SelectionSet
    OperationType:
    querymutationsubscription
    GraphQL 建模了三种类型的操作:
    query – 只读获取。
    mutation——先写后取。
    subscription – 一种长期请求,用于响应源事件来获取数据。
    每个操作都由可选的操作名称和选择集表示。
    例如,此突变操作可能“喜欢”一个故事,然后检索新的喜欢数量:
    例子5mutation {
      likeStory(storyID: 12345) {
        story {
          likeCount
        }
      }
    }
    查询简写
    如果文档仅包含一个操作,并且该操作是不定义变量且不包含指令的查询,则该操作可以以省略查询关键字和操作名称的简写形式表示。
    例如,这个未命名的查询操作是通过查询简写编写的。
    例子6{
      field
    }
    注意:下面的许多示例将使用查询的简写语法。

    2.4 选择集#

    SelectionSet:
    {Selectionlist}
    Selection:
    Field
    FragmentSpread
    InlineFragment
    一个操作选择它所需的信息集,并且将准确地接收到这些信息,而不会多取或少取数据。
    Example № 7
    Example № 7{
      id
      firstName
      lastName
    }
    在这个查询操作中, id , firstName 和 lastName 字段构成了一个选择集。选择集还可以包含片段引用。
    also contain fragment references.

    2.5 领域#

    Field
    AliasoptNameArgumentsoptDirectivesoptSelectionSetopt
    选择集主要由字段组成。字段描述了选择集中可用于请求的一条离散信息。
    某些字段描述复杂的数据或与其他数据的关系。为了进一步探索这些数据,字段本身可能包含一个选择集,允许深度嵌套的请求。所有 GraphQL 操作都必须将其选择指定为返回标量值的字段,以确保响应的形状明确。
    例如,此操作选择复杂数据和关系的字段直至标量值。
    示例#8{
      me {
        id
        firstName
        lastName
        birthday {
          month
          day
        }
        friends {
          name
        }
      }
    }
    操作的顶级选择集中的字段通常表示应用程序及其当前查看器可全局访问的一些信息。这些顶部字段的一些典型示例包括对当前登录查看者的引用,或访问由唯一标识符引用的某些类型的数据。
    例子9# `me` could represent the currently logged in viewer.
    {
      me {
        name
      }
    }
    
    # `user` represents one of many users in a graph of data, referred to by a
    # unique identifier.
    {
      user(id: 4) {
        name
      }
    }

    2.6 论点#

    ArgumentsConst:
    (ArgumentConstlist)
    ArgumentConst:
    Name:ValueConst
    字段在概念上是返回值的函数,偶尔接受改变其行为的参数。这些参数通常直接映射到 GraphQL 服务实现中的函数参数。
    在此示例中,我们要查询特定用户(通过参数请求id)及其特定的个人资料图片size:
    示例 10{
      user(id: 4) {
        id
        name
        profilePic(size: 100)
      }
    }
    给定字段可以存在许多参数:
    例子11{
      user(id: 4) {
        id
        name
        profilePic(width: 100, height: 50)
      }
    }
    参数是无序的
    参数可以按任何句法顺序提供并保持相同的语义。
    这两个操作在语义上是相同的:
    示例 12{
      picture(width: 200, height: 100)
    }
    例子13{
      picture(height: 100, width: 200)
    }

    2.7 字段别名#

    Alias 
    Name:
    默认情况下,响应对象中字段的响应键将使用该字段的名称。但是,您可以通过指定别名来定义不同的响应键。
    在此示例中,我们可以获取两张不同大小的个人资料图片,并确保生成的响应对象不会有重复的键:
    示例 14{
      user(id: 4) {
        id
        name
        smallPic: profilePic(size: 64)
        bigPic: profilePic(size: 1024)
      }
    }
    返回结果:
    示例 15{
      "user": {
        "id": 4,
        "name": "Mark Zuckerberg",
        "smallPic": "https://cdn.site.io/pic-4-64.jpg",
        "bigPic": "https://cdn.site.io/pic-4-1024.jpg"
      }
    }
    操作顶层的字段也可以指定一个别名:
    示例 16{
      zuck: user(id: 4) {
        id
        name
      }
    }
    返回结果:
    示例 17{
      "zuck": {
        "id": 4,
        "name": "Mark Zuckerberg"
      }
    }

    2.8 片段#

    FragmentSpread:
    ... FragmentNameDirectivesopt
    FragmentDefinition:
    fragmentFragmentNameTypeConditionDirectivesoptSelectionSet
    FragmentName:
    Name but not on
    片段是 GraphQL 中的主要组成单元。
    片段允许重复使用常见的重复选择字段,从而减少文档中的重复文本。内联片段可以直接在选择中使用,以在针对接口或联合进行查询时以类型条件为条件。
    例如,如果我们想获取有关共同朋友以及某个用户的朋友的一些常见信息:
    示例 18query noFragments {
      user(id: 4) {
        friends(first: 10) {
          id
          name
          profilePic(size: 50)
        }
        mutualFriends(first: 10) {
          id
          name
          profilePic(size: 50)
        }
      }
    }
    重复的字段可以提取到片段中并由父片段或操作组成。
    示例 19query withFragments {
      user(id: 4) {
        friends(first: 10) {
          ...friendFields
        }
        mutualFriends(first: 10) {
          ...friendFields
        }
      }
    }
    
    fragment friendFields on User {
      id
      name
      profilePic(size: 50)
    }
    通过使用扩展运算符( ... ),可以消耗片段。片段选择的所有字段将被添加到与片段调用相同级别的字段选择中。这是通过多个级别的片段扩展来实现的。
    例如:
    示例 20query withNestedFragments {
      user(id: 4) {
        friends(first: 10) {
          ...friendFields
        }
        mutualFriends(first: 10) {
          ...friendFields
        }
      }
    }
    
    fragment friendFields on User {
      id
      name
      ...standardProfilePic
    }
    
    fragment standardProfilePic on User {
      profilePic(size: 50)
    }
    操作noFragments、withFragments、 和withNestedFragments都生成相同的响应对象。
    操作noFragments、withFragments、 和withNestedFragments都生成相同的响应对象。

    2.8.1 类型条件#

    TypeCondition:
    on NamedType
    片段必须指定它们适用的类型。在此示例中,friendFields 可以在查询 的上下文中使用 User。
    不能在任何输入值(标量、枚举或输入对象)上指定片段。
    可以在对象类型、接口和联合上指定片段。
    仅当正在操作的对象的具体类型与片段的类型匹配时,片段内的选择才会返回值。
    例如,在此操作中使用 Facebook 数据模型:
    示例 21query FragmentTyping {
      profiles(handles: ["zuck", "coca-cola"]) {
        handle
        ...userFragment
        ...pageFragment
      }
    }
    
    fragment userFragment on User {
      friends {
        count
      }
    }
    
    fragment pageFragment on Page {
      likers {
        count
      }
    }
    profiles 根字段返回一个列表,其中每个元素可以是 Page 或 User 。当 profiles 结果中的对象是 User 时,将存在 friends 而 likers 将不存在。相反,当结果是 Page 时,将存在 likers 而 friends 将不存在。
    示例 22{
      "profiles": [
        {
          "handle": "zuck",
          "friends": { "count": 1234 }
        },
        {
          "handle": "coca-cola",
          "likers": { "count": 90234512 }
        }
      ]
    }

    2.8.2 内联片段#

    InlineFragment:
    ...TypeConditionopt Directivesopt SelectionSet
    片段可以在选择集中内嵌定义。这样做是为了根据运行时类型有条件地包含字段。标准片段包含的这一特征在query FragmentTyping示例中得到了演示。我们可以使用内联片段完成同样的事情。
    示例 23query inlineFragmentTyping {
      profiles(handles: ["zuck", "coca-cola"]) {
        handle
        ... on User {
          friends {
            count
          }
        }
        ... on Page {
          likers {
            count
          }
        }
      }
    }
    内联片段也可用于将指令应用于一组字段。如果省略 TypeCondition,则内联片段被视为与封闭上下文具有相同类型。
    示例 24query inlineFragmentNoType($expandedInfo: Boolean) {
      user(handle: "zuck") {
        id
        name
        ... @include(if: $expandedInfo) {
          firstName
          lastName
          birthday
        }
      }
    }

    2.9 输入值#

    Value Const:
    if not const Variable
    ConstVariable
    IntValue
    FloatValue
    StringValue
    BooleanValue
    NullValue
    EnumValue
    ListValue Const
    ObjectValue Const
    字段和指令参数接受各种文字原语的输入值;输入值可以是标量、枚举值、列表或输入对象。
    如果未定义为常量(例如,在DefaultValue中),则可以将输入值指定为变量。列表和输入对象还可以包含变量(除非定义为常量)。

    2.9.1 整数值#

    IntValue:
    IntegerPartDigit.NameStart
    IntegerPart:
    NegativeSignopt 0
    NegativeSign opt NonZeroDigitDigit list,opt
    NegativeSign
    NonZeroDigit:
    Digit but not 0
    IntValue 指定时不带小数点或指数,但可以为负数(例如-123)。它不能有任何前导 0。
    IntValue 后面不能跟 Digit。换句话说,IntValue 令牌始终是最长的可能有效序列。源字符 12 不能被解释为两个标记,因为 1 后面跟着数字 2。这也意味着源 00 无效,因为它既不能被解释为单个标记,也不能被解释为两个 0 标记。
    IntValue 后面不能跟着.或 NameStart。如果后面跟着.或 ExponentIndicator,则该标记只能被解释为可能的FloatValue。不能跟着其他NameStart字符。例如,序列 0x123 和 123L 没有有效的词法表示。

    2.9.2 浮动值#

    FloatValue:
    IntegerPart FractionalPart ExponentPartDigit.NameStart
    IntegerPart FractionalPart Digit.NameStart
    IntegerPart ExponentPart Digit.NameStart
    FractionalPart:
    .Digit list
    ExponentPart:
    ExponentIndicator Sign opt Digit list
    ExponentIndicator:
    eE
    Sign:
    +-
    FloatValue 包含小数点(例如 1.0)或指数(例如 1e50 )或两者(例如 6.0221413e23),并且可以为负数。与 IntValue 一样,它也不能有任何前导 0。
    FloatValue 后面不能跟 Digit。换句话说,FloatValue 令牌始终是最长的可能有效序列。源字符 1.23 不能解释为两个令牌,因为 1.2 后面跟着数字 3。
    FloatValue 后面不得跟有. 。例如,序列 1.23.4 不能解释为两个标记(1.2,3.4)。
    FloatValue 后面不能跟 NameStart。例如,序列0x1.2p3没有有效的词汇表示。
    注意:数字字面量 IntValue 和 FloatValue 都限制紧跟着一个字母(或其他 NameStart)以减少混淆或意外行为,因为 GraphQL 只支持十进制数。

    2.9.3 布尔值#

    Boolean Value
    turefalse
    两个关键字trueandfalse代表两个布尔值。

    2.9.4 字符串值#

    StringValue:
    """
    "StringCharacterlist"
    """[BlockStringCharacter](https://spec.graphql.org/October2021
    /#BlockStringCharacter) list,opt"""
    StringCharacter:
    SourceCharacter but not "or \ or LineTerminator
    \uEscapedUnicode
    \EscapedCharacter
    Escaped Unicode:
    /[0-9A-Fa-f]{4}/
    EscapedCharacter:
    "\/bfnrt
    BlockStringCharacter
    SourceCharacter but not """ or """
    """
    字符串是用引号 (U+0022) 括起来的字符序列。(例如 “Hello World”)。空格和其他被忽略的字符在字符串值中很重要。
    空字符串 "" 不能紧跟另一个 ",否则它将被解释为块字符串的开始。例如,源代码 """""" 只能被解释为一个空的块字符串,而不是三个空字符串。
    单引号字符串中允许使用非 ASCII Unicode 字符。由于 SourceCharacter 不得包含某些 ASCII 控制字符,因此必须使用转义序列来表示这些字符。\ , "字符也必须转义。所有其他转义序列都是可选的。
    块字符串#
    块字符串是用三引号 ( """) 括起来的字符序列。空格、行终止符、引号和反斜杠字符都可以不转义地使用,以启用逐字文本。字符必须都是有效的 SourceCharacter。
    由于块字符串表示经常在缩进位置使用的自由格式文本,因此块字符串的字符串值语义不包括通过 BlockStringValue ()的统一缩进和空白首行和尾随行。
    例如,以下包含块字符串的操作:
    示例 25mutation {
      sendEmail(message: """
        Hello,
          World!
    
        Yours,
          GraphQL.
      """)
    }
    与标准引用字符串相同:
    示例 26mutation {
      sendEmail(message: "Hello,\n  World!\n\nYours,\n  GraphQL.")
    }
    由于块字符串值会去除前导和尾随空行,因此对于给定值没有单个规范的打印块字符串。由于块字符串通常表示自由格式的文本,因此如果它们以空行开头和结尾,则认为更容易阅读。
    示例 27"""
    This starts with and ends with an empty line,
    which makes it easier to read.
    """
    反例#28"""This does not start with or end with any empty lines,
    which makes it a little harder to read."""
    注意:如果字符串值中需要使用非可打印的 ASCII 字符,则必须使用带有适当转义序列的标准引用字符串,而不是块字符串。
    语义学#
    StringValue:
    ""
    返回一个空序列。
    StringValue
    "StringCharacter list"
    返回所有 StringCharacter 代码点的序列。
    StringCharacter
    SourceCharacter " \ LineTerminator
    返回代码点 SourceCharacter。
    StringCharacter
    \uEscaped Unicode
    将值设为由 EscapedUnicode 中的十六进制数字序列表示的 16 位十六进制值。
    返回代码点值。
    StringCharacter
    根据下表返回由转义字符表示的代码点。
    Escaped CharacterCode PointCharacter Name
    "U+0022double quote
    \U+005Creverse solidus (back slash)
    /U+002Fsolidus (forward slash)
    bU+0008backspace
    fU+000Cform feed
    nU+000Aline feed (new line)
    rU+000Dcarriage return
    tU+0009horizontal tab
    StringValue
    """BlockStringCharacter listopt """
    1.
    将 rawValue 设为所有 BlockStringCharacter Unicode 字符值的 Unicode 字符序列(可能为空序列)。
    2.
    返回 BlockStringValue(rawValue) 的结果。
    SourceCharacter""""""
    1.
    返回SourceCharacter的字符值。
    BlockStringCharacter
    """
    1.
    返回字符序列 """ 。
    BlockStringValue(rawValue)
    1.
    Let lines be the result of splitting rawValue by LineTerminator.
    2.
    Let commonIndent be null.
    3.
    For each line in lines:
    1.
    将 lines 定义为通过 LineTerminator 将 rawValue 拆分的结果。
    2.
    将 commonIndent 设为 null。
    3.
    对于 lines 中的每一行:
    4.
    If indent is less than length:
    a. 如果 line 是 lines 中的第一项,则继续到下一行。
    b. 设长度为行中字符的数量。
    c. 让缩进 (indent) 成为行中连续的前导空白字符的数量。
    d. 如果缩进小于长度:
    1.
    如果 commonIndent 为空或者 indent 小于 commonIndent:
    2.
    将 commonIndent 设为 indent。
    4.
    如果 commonIndent 不为空:
    1.
    对于 lines 中的每一行:
    如果 line 是 lines 中的第一项,则继续到下一行。
    从行首删除 commonIndent 字符。
    5.
    虽然第一行中的第一个项目行只包含空格
    a.从行中删除第一项。
    6.
    当最后一行中的项目行只包含空格时:
    a.从行中删除最后一项。
    7.
    将 formatted 设为空字符序列。
    8.
    对于 lines 中的每一行:
    1.
    如果 line 是 lines 中的第一项:
    a.追加带有换行的格式。
    2.
    否则:
    a.追加格式化,并附加换行符(U+000A)。
    b.追加带有换行的格式。

    2.9.5 空值#

    Null Value:
    null
    空值用关键字 null 表示。
    GraphQL 有两种语义上不同的方式来表示缺少值:
    显式提供文字值:null。
    隐含地根本不提供任何价值。
    例如,这两个字段调用相似,但不相同:
    示例 29{
      field(arg: null)
      field
    }
    第一个已显式向参数 “arg” 提供 null ,而第二个则隐式未向参数 “arg” 提供值。这两种形式可以有不同的解释。例如,突变分别表示删除字段与不更改字段。这两种形式都不能用于期望非空类型的输入。
    注意:通过变量,可以使用相同的两种方法来表示缺少值,即将变量值设置为 null 或根本不提供变量值。

    2.9.6 Enum Value#

    枚举值
    Name but not true of fasle or null
    枚举值表示为不带引号的名称(例如MOBILE_WEB)。建议枚举值“全部大写”。枚举值仅在已知精确枚举类型的上下文中使用。因此,没有必要在文字中提供枚举类型名称。

    2.9.7 列表值#

    ListValueConst ListValueConst:
    []
    [ValueConstlist] [ValueConstlist]
    列表是用方括号括起来的有序值序列[ ]。列表文字的值可以是任何值文字或变量(例如[1, 2, 3])。
    逗号在整个 GraphQL 中是可选的,因此允许使用尾随逗号,并且重复的逗号不代表缺失值。
    语义学#
    ListValue
    1.
    返回一个新的空列表值。
    ListValue
    1.
    令inputList为新的空列表值。
    2.
    对于每个值列表
    a. 令value为评估Value的结果。
    b. 将值附加到inputList。
    3.
    返回输入列表

    2.9.8 输入对象值#

    ObjectValueConst:
    {}
    {ObjectFieldConst,list}
    ObjectFieldConst
    Name:ValueConst
    输入对象字面量值是用大括号括起来的键控输入值的无序列表{ }。对象文字的值可以是任何输入值文字或变量(例如{ name: "Hello world", score: 1.0 })。我们将输入对象的文字表示称为“对象文字”。
    输入对象字段无序#
    输入对象字段可以按任何语法顺序提供并保持相同的语义。
    这两个操作在语义上是相同的:
    示例 30{
      nearestThing(location: { lon: 12.43, lat: -53.211 })
    }
    示例#31{
      nearestThing(location: { lat: -53.211, lon: 12.43 })
    }
    语义学#
    ObjectValue{}
    1.
    返回不带字段的新输入对象值。
    对象值
    {对象字段列表}
    1.
    令inputObject为不带字段的新输入对象值。
    2.
    对于ObjectField列表中的每个字段
    a. 让name为 Name in field。
    b. 令value为评估``field中 Value 的结果。``
    c. 向名为name 的inputObject添加一个包含值value的字段。````
    3.
    返回输入对象

    2.10 变量#

    Variable:
    $Name
    VariableDefinitions:
    (VariableDefinitionlist)
    VariableDefinition:
    Variable: TypeDefaultValueoptDirectivesConstopt
    DefaultValue:
    =ValueConst
    GraphQL 操作可以使用变量进行参数化,从而最大限度地提高重用性,并避免在运行时在客户端中构建昂贵的字符串。
    如果未定义为常量(例如,在 DefaultValue 中),则可以为输入值提供变量。
    变量必须在操作的顶部定义,并且在该操作的整个执行过程中都处于作用域内。这些变量的值作为请求的一部分提供给 GraphQL 服务,以便在执行期间可以替换它们。
    在此示例中,我们希望根据特定设备的大小获取个人资料图片大小:
    示例#32query getZuckProfile($devicePicSize: Int) {
      user(id: 4) {
        id
        name
        profilePic(size: $devicePicSize)
      }
    }
    如果为变量值提供 JSON,我们可以请求 a profilePicof size 60:
    示例#33{
      "devicePicSize": 60
    }
    片段内的变量使用#
    变量可以在片段内使用。变量对于给定的操作具有全局作用域,因此片段中使用的变量必须在任何传递使用该片段的顶级操作中声明。如果片段中引用了某个变量,并且该变量被未定义该变量的操作所包含,则该操作无效(请参阅所有变量使用定义)。

    2.11 类型参考#

    Type:
    NamedType
    ListType
    NonNullType
    NamedType
    Name
    ListType
    [Type]
    NonNullType
    NamedType!
    ListType!
    GraphQL 描述了参数和变量期望的数据类型。输入类型可以是另一个输入类型的列表,或者任何其他输入类型的非空变体。
    语义学#
    Type:Name
    1.
    令name为Name 的字符串值
    2.
    令type为 Schema命名中定义的类型 name
    3.
    类型不能为空
    4.
    返回类型
    Type:[Type]
    1.
    令itemType为计算 Type的结果
    2.
    令type为 List 类型,其中itemType为包含的类型。
    3.
    返回类型
    Type:Type!
    1.
    令nullableType为计算 Type 的结果
    2.
    令type为非空类型,其中nullableType是包含的类型。
    3.
    返回类型

    2.12 指令#

    DirectivesConst:
    DirectiveConstlist
    DirectiveConst:
    @NameArgumentsConstopt
    指令提供了一种描述 GraphQL 文档中备用运行时执行和类型验证行为的方法。
    在某些情况下,您需要提供选项来改变 GraphQL 的执行行为,而字段参数无法满足要求,例如有条件地包含或跳过字段。指令通过向执行者描述附加信息来提供这一点。
    指令有一个名称以及一个可以接受任何输入类型的值的参数列表。
    指令可用于描述类型、字段、片段和操作的附加信息。
    随着 GraphQL 的未来版本采用新的可配置执行功能,它们可能会通过指令公开。GraphQL 服务和工具还可以提供超出此处描述的任何其他自定义指令。
    指令顺序很重要#
    指令可以以可以具有语义解释的特定句法顺序来提供。
    这两种类型定义可能具有不同的语义:
    示例#34type Person
      @addExternalFields(source: "profiles")
      @excludeField(name: "photo") {
      name: String
    }
    示例 35type Person
      @excludeField(name: "photo")
      @addExternalFields(source: "profiles") {
      name: String
    }

    3 类型系统#

    GraphQL 类型系统描述了 GraphQL 服务的功能,用于确定请求的操作是否有效,保证响应结果的类型,并描述变量的输入类型以确定请求时提供的值是否有效。
    TypeSystemDocument:
    TypeSystemDefinitionlist
    TypeSystemDefinition:
    SchemaDefinition
    TypeDefinition
    DirectiveDefinition
    GraphQL 语言包含用于描述 GraphQL 服务的类型系统的 IDL 。工具可以使用此定义语言来提供诸如客户端代码生成或服务引导之类的实用程序。
    仅寻求执行 GraphQL 请求而不构建新的 GraphQL 模式的 GraphQL 工具或服务可能会选择不允许TypeSystemDefinition。仅寻求生成模式而不执行请求的工具可以选择仅允许 TypeSystemDocument 而不允许 ExecutableDefinition 或 TypeSystemExtension,但应提供描述性错误(如果存在)。
    注意:在说明示例类型系统时,本规范文档的其余部分都使用类型系统定义语言。

    3.1类型系统扩展#

    TypeSystemExtensionDocument:
    TypeSystemDefinitionOrExtensionlist
    TypeSystemDefinitionOrExtension:
    TypeSystemDefinition
    TypeSystemExtension
    TypeSystemExtension:
    SchemaExtension
    TypeExtension
    类型系统扩展用于表示从某些原始类型系统扩展而来的 GraphQL 类型系统。例如,本地服务可能会使用它来表示 GraphQL 客户端仅在本地访问的数据,或者本身就是另一个 GraphQL 服务扩展的 GraphQL 服务。
    仅寻求生成和扩展架构而不执行请求的工具可以选择仅允许 TypeSystemExtensionDocument 而不允许 ExecutableDefinition,但应提供描述性错误(如果存在)。

    3.2 描述#

    Description:
    StringValue
    文档是 GraphQL 类型系统的一流功能。为了确保 GraphQL 服务的文档与其功能保持一致,GraphQL 定义的描述与其定义一起提供,并通过内省提供。
    为了让 GraphQL 服务设计者能够轻松发布文档以及 GraphQL 服务的功能,GraphQL 描述是使用 Markdown 语法(由CommonMark指定)定义的。在类型系统定义语言中,这些描述字符串(通常是BlockString)紧接在它们描述的定义之前出现。
    GraphQL 模式和所有其他可描述的定义(例如类型、字段、参数等)应提供描述,除非它们被认为是自我描述性的。
    作为一个例子,这个简单的 GraphQL 模式得到了很好的描述:
    示例#36"""
    A simple GraphQL schema which is well described.
    """
    schema {
      query: Query
    }
    
    """
    Root type for all your query operations
    """
    type Query {
      """
      Translates a string from a given language into a different language.
      """
      translate(
        "The original language that `text` is provided in."
        fromLanguage: Language
    
        "The translated language to be returned."
        toLanguage: Language
    
        "The text to be translated."
        text: String
      ): String
    }
    
    """
    The set of languages supported by `translate`.
    """
    enum Language {
      "English"
      EN
    
      "French"
      FR
    
      "Chinese"
      CH
    }

    3.3 模式#

    SchemaDefinition:
    描述选项图式指令 常量选项 {RootOperationTypeDefinition 列表}
    RootOperationTypeDefinition:
    操作类型:命名类型
    GraphQL 服务的集体类型系统功能称为该服务的“架构”。模式是根据它支持的类型和指令以及每种操作的根操作类型来定义的:查询、突变和订阅;这决定了类型系统中这些操作开始的位置。
    GraphQL 模式本身必须在内部有效。本节描述了相关验证过程的规则。
    GraphQL 架构中的所有类型都必须具有唯一的名称。所提供的两个类型不能具有相同的名称。所提供的类型的名称不得与任何内置类型(包括标量和内省类型)冲突。
    GraphQL 模式中的所有指令都必须具有唯一的名称。
    模式中定义的所有类型和指令不得具有以“__” (两个下划线)开头的名称,因为这是 GraphQL 的自省系统专用的。
    用的。

    3.3.1 根操作类型#

    模式定义了它支持的每种操作的初始根操作类型:查询、变更和订阅;这决定了类型系统中这些操作开始的位置。
    必须提供查询根操作类型,并且必须是对象类型。
    变更根操作类型可选;如果不提供,则该服务不支持突变。如果提供,它必须是对象类型。
    同样,订阅根操作类型也是可选的;如果不提供,该服务不支持订阅。如果提供,它必须是对象类型。
    如果提供,查询、突变和订阅根类型必须全部是不同的类型。
    查询根操作类型上的字段指示 GraphQL 查询操作顶层可用的字段。
    例如,这个示例操作:
    示例 37query {
      myName
    }
    仅当查询根操作类型具有名为“myName”的字段时才有效:
    示例#38type Query {
      myName: String
    }
    类似地,只有当突变根操作类型具有名为“setName”的字段时,以下突变才有效。
    示例#39mutation {
      setName(name: "Zuck") {
        newName
      }
    }
    使用类型系统定义语言时,文档必须最多包含一个架构定义。
    在此示例中,GraphQL 模式是使用查询和突变根操作类型定义的:
    示例 40schema {
      query: MyQueryRootType
      mutation: MyMutationRootType
    }
    
    type MyQueryRootType {
      someField: String
    }
    
    type MyMutationRootType {
      setSomeField(to: String): String
    }
    默认根操作类型名称#
    虽然任何类型都可以是 GraphQL 操作的根操作类型,但当 query、mutation 和 subscription 根类型分别命名为 “Query”、“Mutation” 和 “Subscription” 时,类型系统定义语言可以省略模式定义。
    同样,当使用类型系统定义语言表示 GraphQL 模式时,如果仅使用默认根操作类型名称,则应省略模式定义。
    此示例描述了一个有效的完整 GraphQL 架构,尽管没有明确包含架构定义。“查询”类型被假定为模式的查询根操作类型。
    示例 41type Query {
      someField: String
    }

    3.3.2 模式扩展#

    SchemaExtension:
    延长图式指令常量选项{RootOperationTypeDefinition列表}
    延长图式指令常量
    模式扩展用于表示从原始模式扩展的模式。例如,这可能由 GraphQL 服务使用,该服务向现有模式添加额外的操作类型或额外的指令。
    注意:没有附加操作类型定义的模式扩展后面不能跟{(例如查询简写),以避免解析歧义。同样的限制也适用于下面的类型定义和扩展。
    模式验证#
    如果定义不正确,架构扩展可能会无效。
    1.
    架构必须已经定义。
    2.
    提供的任何不可重复指令不得已应用于原始架构。

    3.4 类型#

    类型定义:
    标量类型定义
    对象类型定义
    接口类型定义
    联合类型定义
    枚举类型定义
    输入对象类型定义
    任何 GraphQL Schema 的基本单位都是类型。GraphQL 中有六种命名类型定义和两种包装类型。
    最基本的类型是Scalar. 标量表示原始值,如字符串或整数。通常,标量场的可能响应是可枚举的。GraphQLEnum在这些情况下提供了一种类型,其中该类型指定有效响应的空间。
    标量和枚举形成响应树中的叶子;中间级别是Object类型,它定义一组字段,其中每个字段是系统中的另一种类型,允许定义任意类型层次结构。
    GraphQL 支持两种抽象类型:接口(Interface)和联合(Union)。
    AnInterface定义字段列表;Object类型和实现此接口的其他接口类型保证实现这些字段。每当一个字段声明它将返回一个接口类型时,它将在执行期间返回一个有效的实现对象类型。
    AUnion定义了可能类型的列表;与接口类似,每当类型系统声明将返回联合时,就会返回可能的类型之一。
    最后,提供复杂的结构作为 GraphQL 字段参数或变量的输入通常很有用;该Input Object类型允许模式准确定义所需的数据。

    3.4.1 包装类型#

    到目前为止,所有类型都被假定为可为空且为单数:例如,标量字符串返回 null 或单数字符串。
    GraphQL 模式可以描述一个字段代表另一种类型的列表;List为此原因提供了该类型,并包装了另一个类型。
    类似地,该Non-Null类型包装另一个类型,并表示结果值永远不会为空(并且字段错误不能导致空值)。
    这两种类型称为“包装类型”;非包装类型称为“命名类型”。包装类型具有底层命名类型,通过不断地展开类型直到找到命名类型来找到该类型。

    3.4.2 输入和输出类型#

    整个 GraphQL 中都使用类型来描述作为参数和变量的输入接受的值以及字段输出的值。这两个用途将类型分类为输入类型和输出类型。某些类型,例如标量和枚举类型,既可以用作输入类型也可以用作输出类型;其他种类的类型只能使用其中一种。输入对象类型只能用作输入类型。对象、接口和联合类型只能用作输出类型。列表和非空类型可以用作输入类型或输出类型,具体取决于包装类型的使用方式。
    IsInputType(类型):
    1.
    如果类型是 List 类型或 Non-Null 类型:
    a. 令unwrappedType为type的未包装类型。
    b. 返回 IsInputType( unwrappedType )
    2.
    如果类型是标量、枚举或输入对象类型:
    a. 返回真
    3.
    返回错误
    输出类型(类型)
    1.
    如果类型是List类型或Non-Null类型:
    a. 令unwrappedType为type的未包装类型。
    b. 返回 IsOutputType( unwrappedType )
    2.
    如果类型是标量、对象、接口、联合或枚举类型:
    a. 返回真
    3.
    返回错误

    3.4.3 类型扩展#

    类型扩展:
    标量类型扩展
    对象类型扩展
    接口类型扩展
    联合类型扩展
    枚举类型扩展
    输入对象类型扩展
    类型扩展用于表示从某些原始类型扩展而来的 GraphQL 类型。例如,本地服务可能会使用它来表示 GraphQL 客户端仅在本地访问的其他字段。
    GraphQL 客户端仅在本地访问的其他字段。

    3.5 标量#

    标量类型定义:
    描述(可选)标量名称指令常量(可选)
    标量类型表示 GraphQL 类型系统中的原始叶值。GraphQL 响应采用分层树的形式;该树的叶子通常是 GraphQL 标量类型(但也可能是枚举类型或空值)。
    GraphQL 提供了许多内置标量,这些标量在下面的部分中进行了完整定义,但是类型系统也可能添加其他自定义标量以引入其他语义。
    内置标量#
    GraphQL 指定了一组基本的定义良好的标量类型:Int、Float、String、Boolean 和 ID。GraphQL 框架应该支持所有这些类型,并且通过这些名称提供类型的 GraphQL 服务必须遵守本文档中为它们描述的行为。例如,服务不得包含名为 Int 的类型并使用它来表示 64 位数字、国际化信息或本文档中定义之外的任何内容。
    从内省类型返回类型集时__Schema,必须包含所有引用的内置标量。如果架构中的任何位置都没有引用内置标量类型(没有该类型的字段、参数或输入字段),则不得包含它。
    当使用类型系统定义语言表示 GraphQL 模式时,为了简洁起见,必须省略所有内置标量。
    自定义标量#
    除了内置标量之外,GraphQL 服务还可以使用自定义标量类型。例如,GraphQL 服务可以定义一个名为 的标量UUID,该标量在序列化为字符串时符合 RFC 4122。当查询 类型的字段时UUID,您可以依靠使用符合 RFC 4122 的解析器解析结果的能力。潜在有用的自定义标量的另一个示例是URL,它序列化为字符串,但由服务器保证是有效的 URL。
    在定义自定义标量时,GraphQL 服务应通过 @specifiedBy 指令或 specifiedByURL 内省字段提供一个标量规范的 URL。这个 URL 必须链接到一个人类可读的规范,描述了该标量的数据格式、序列化和强制转换规则。
    例如,提供UUID标量的 GraphQL 服务可以链接到 RFC 4122,或定义该 RFC 的合理子集的某个自定义文档。如果存在标量规范 URL ,则了解它的系统和工具应该符合其描述的规则。
    示例 42scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
    scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
    自定义标量规范 URL应提供单一、稳定的格式以避免歧义。如果链接的规范不断变化,则服务应链接到固定版本而不是可能更改的资源。
    自定义标量规范 URL 一旦定义就不应更改。这样做可能会破坏工具或可能在链接规范的内容中引入重大更改。
    内置标量类型不得提供本文档指定的标量规范 URL 。
    注意:自定义标量还应该总结指定的格式并在其描述中提供示例。
    结果强制和序列化#
    GraphQL 服务在准备给定标量类型的字段时,必须维护标量类型描述的约定,方法是强制该值,或者在无法强制强制值或强制可能导致数据丢失时产生字段错误。
    GraphQL 服务可能决定允许将不同的内部类型强制为预期的返回类型。例如,当强制转换Int类型的字段时,布尔真值可能会生成1,或者字符串值“123”可能会被解析为基数 10 123。但是,如果内部类型强制不能在不丢失信息的情况下合理执行,那么它一定会引发字段错误。
    由于 GraphQL 服务的客户端无法观察到这种强制行为,因此强制的精确规则留给了实现。唯一的要求是服务必须产生符合预期标量类型的值。
    GraphQL 标量根据所使用的序列化格式进行序列化。对于每个给定的标量类型可能有一个最合适的序列化原语,并且服务应该在适当的情况下生成每个原语。
    有关常见 JSON 和其他格式的标量序列化的更多详细信息,请参阅序列化格式。
    输入强制#
    如果 GraphQL 服务期望标量类型作为参数的输入,则可以观察到强制转换,并且必须明确定义规则。如果输入值与强制规则不匹配,则必须引发请求错误(在执行开始之前对输入值进行验证)。
    GraphQL 有不同的常量文字来表示整数和浮点输入值,并且根据遇到的输入值类型,强制规则可能会有所不同。GraphQL 可以通过变量进行参数化,当通过 HTTP 等传输方式发送时,变量的值通常会被序列化。由于一些常见的序列化(例如 JSON)不区分整数和浮点值,因此如果它们具有空小数部分(例如 ),则它们被解释为整数输入值,否则解释为浮点输入值1.0。
    对于以下所有类型,除了 Non-Null 之外,如果提供显式值 null ,则输入强制的结果为 null。

    3.5.1 INT#

    Int 标量类型表示带符号的 32 位数字非小数值。支持 32 位整数或数字类型的响应格式应使用该类型来表示该标量。
    结果强制#
    返回 Int 类型的字段期望遇到 32 位整数内部值。
    GraphQL 服务可能会在合理的情况下将非整数内部值强制转换为整数,而不会丢失信息,否则它们必须引发字段错误。此类示例可能包括返回1浮点数1.0或返回123字符串"123"。在强制可能丢失数据的情况下,引发字段错误更合适。例如,浮点数1.2应该引发字段错误,而不是被截断为1.
    如果整数内部值表示小于 -2 31 或大于或等于 2 31 的值,则应引发字段错误。
    输入强制#
    当预期作为输入类型时,仅接受整数输入值。所有其他输入值(包括具有数字内容的字符串)都必须引发请求错误,指示类型不正确。如果整数输入值表示小于-2 31 或大于或等于2 31 的值,则应引发请求错误。
    注意:大于 32 位的数字整数值应使用 String 或自定义的标量类型,因为并非所有平台和传输都支持编码大于 32 位的整数。

    3.5.2 Float#

    Float 标量类型表示 IEEE 754指定的有符号双精度有限值。支持适当双精度数字类型的响应格式应使用该类型来表示该标量。
    结果强制#
    返回Float类型的字段预计会遇到双精度浮点内部值。
    GraphQL 服务可能会在合理的情况下将非浮点内部值强制转换为Float,而不会丢失信息,否则它们必须引发字段错误。此类示例可能包括返回1.0整数1或123.0字符串"123"。
    非有限浮点内部值(NaN 和 Infinity)不能强制为 Float 并且必须引发字段错误。
    输入强制#
    当预期作为输入类型时,整数和浮点输入值都被接受。通过添加空小数部分将整数输入值强制转换为 Float,例如1.0对于整数输入值1。所有其他输入值(包括具有数字内容的字符串)都必须引发请求错误,指示类型不正确。如果输入值以其他方式表示有限 IEEE 754 无法表示的值(例如 NaN、Infinity 或超出可用精度的值),则必须引发请求错误。

    3.5.3 String#

    String 标量类型表示文本数据,表示为 Unicode 代码点序列。GraphQL 最常使用 String 类型来表示自由格式的人类可读文本。字符串的内部编码方式(例如 UTF-8)留给服务实现。所有响应序列化格式都必须支持字符串表示形式(例如,JSON Unicode 字符串),并且必须使用该表示形式来序列化此类型。
    结果强制#
    返回String类型的字段期望遇到 Unicode 字符串值。
    GraphQL 服务可能会在合理的情况下将非字符串原始值强制转换为String ,而不会丢失信息,否则它们必须引发字段错误。"true"此类示例可能包括返回布尔真值的字符串或"1"整数的字符串1。
    输入强制#
    当预期作为输入类型时,仅接受有效的 Unicode 字符串输入值。所有其他输入值必须引发请求错误,指示类型不正确。

    3.5.4 布尔值#

    布尔标量类型表示trueor false。如果支持,响应格式应使用内置布尔类型;否则,他们应该使用整数1和的表示0。
    结果强制#
    返回布尔类型的字段期望遇到布尔内部值。
    GraphQL 服务可能会在合理的情况下将非布尔原始值强制转换为布尔值,而不会丢失信息,否则它们必须引发字段错误。这方面的示例可能包括返回true非零数字。
    输入强制#
    当预期作为输入类型时,仅接受布尔输入值。所有其他输入值必须引发请求错误,指示类型不正确。

    3.5.5 ID#

    ID 标量类型表示唯一标识符,通常用于重新获取对象或作为缓存的键。ID 类型的序列化方式与 String 相同;然而,它并不适合人类可读。虽然它通常是数字,但它应该始终序列化为 String。
    结果强制#
    GraphQL 与 ID 格式无关,并序列化为字符串以确保 ID 可以表示的多种格式的一致性,从小的自动增量数字到大的 128 位随机数,再到 Base64 编码值或 GUID 等格式的字符串值。
    GraphQL 服务应该根据它们期望的 ID 格式进行适当的强制。当强制不可能时,它们必须引发字段错误。
    输入强制#
    当预期作为输入类型时,任何字符串(例如"4")或整数(例如4或-4)输入值都应强制转换为 ID,以适合给定 GraphQL 服务所需的 ID 格式。任何其他输入值,包括浮点输入值(例如4.0),都必须引发请求错误,指示类型不正确。

    3.5.6 标量扩展#

    ScalarTypeExtension:
    扩展标量类型 scalarName 的指令常量。
    标量类型扩展用于表示从某些原始标量类型扩展而来的标量类型。例如,这可能由 GraphQL 工具或服务使用,向现有标量添加指令。
    类型验证#
    如果定义不正确,标量类型扩展可能会无效。
    1.
    命名类型必须已定义并且必须是标量类型。
    2.
    提供的任何不可重复指令不得已应用于原始标量类型。

    3.6 对象#

    ObjectTypeDefinition :
    描述(可选)类型名 实现接口(可选)指令(可选)字段定义(可选)
    描述(可选)类型名 实现接口(可选)指令(可选){
    实现接口:
    实现接口 & 命名类型
    implements & 命名类型(可选)
    字段定义:
    { 字段定义列表 }
    字段定义:
    描述(可选)名称 参数定义(可选):类型 指令(可选)常量(可选)
    GraphQL 操作是分层且组合的,描述信息树。标量类型描述这些分层操作的叶值,而对象描述中间级别。
    GraphQL 对象表示命名字段的列表,每个字段都会生成特定类型的值。对象值应序列化为有序映射,其中选定的字段名称(或别名)是键,评估字段的结果是值,按它们在选择集中出现的顺序排序。
    对象类型中定义的所有字段不得具有以“__” (两个下划线)开头的名称,因为这是 GraphQL 的自省系统专用的。
    例如,类型Person可以描述为:
    示例 43type Person {
      name: String
      age: Int
      picture: Url
    }
    其中name是一个将产生String值的字段,age是一个将产生Int值的字段,picture是一个将产生Url值的字段。
    查询对象值必须至少选择一个字段。字段的选择将生成一个有序映射,其中恰好包含所查询对象的子集,该子集应按照查询的顺序表示。只有在对象类型上声明的字段才可以在该对象上有效查询。
    例如,选择 的所有字段Person:
    示例 44{
      name
      age
      picture
    }
    会产生对象:
    示例 45{
      "name": "Mark Zuckerberg",
      "age": 30,
      "picture": "http://some.cdn/picture.jpg"
    }
    选择字段子集时:
    示例 46{
      age
      name
    }
    必须仅产生该子集​​:
    示例 47{
      "age": 30,
      "name": "Mark Zuckerberg"
    }
    对象类型的字段可以是标量、枚举、其他对象类型、接口或联合。此外,它可以是任何其底层基本类型是这五种类型之一的包装类型。
    例如,该Person类型可能包括relationship:
    示例 48type Person {
      name: String
      age: Int
      picture: Url
      relationship: Person
    }
    有效的操作必须为返回对象的任何字段提供嵌套字段集,因此此操作无效:
    反例第 49 号{
      name
      relationship
    }
    然而,这个例子是有效的:
    示例 50{
      name
      relationship {
        name
      }
    }
    并将产生查询的每个对象类型的子集:
    示例 51{
      "name": "Mark Zuckerberg",
      "relationship": {
        "name": "Priscilla Chan"
      }
    }
    现场订购#
    当查询一个对象时,字段的映射顺序的概念上与执行过程中遇到的顺序相同,不包括类型不适用的片段以及通过 @skip 或 @include 指令跳过的字段或片段。使用 CollectFields() 算法可以正确产生这个顺序。
    能够表示有序映射的响应序列化格式应保持此顺序。只能表示无序映射的序列化格式(例如 JSON)应保留此文本顺序。也就是说,如果{foo, bar}按该顺序查询两个字段,则生成的 JSON 序列化应包含{"foo": "...", "bar": "..."}相同的顺序。
    生成一个响应,其中字段的表示顺序与它们在请求中出现的顺序相同,可以提高调试期间的可读性,并且如果可以预期属性的顺序,则可以更有效地解析响应。
    如果片段在其他字段之前展开,则片段指定的字段出现在响应中的以下字段之前。
    示例 52{
      foo
      ...Frag
      qux
    }
    
    fragment Frag on Query {
      bar
      baz
    }
    产生有序结果:
    示例 53{
      "foo": 1,
      "bar": 2,
      "baz": 3,
      "qux": 4
    }
    如果在选择中多次查询某个字段,则按第一次遇到的顺序排序。但是,类型不适用的片段不会影响排序。
    示例 54{
      foo
      ...Ignored
      ...Matching
      bar
    }
    
    fragment Ignored on UnknownType {
      qux
      baz
    }
    
    fragment Matching on Query {
      bar
      qux
      foo
    }
    产生有序结果:
    示例 55{
      "foo": 1,
      "bar": 2,
      "qux": 3
    }
    此外,如果指令导致字段被排除,则在字段排序中不会考虑它们。
    示例 56{
      foo @skip(if: true)
      bar
      foo
    }
    产生有序结果:
    示例 57{
      "bar": 1,
      "foo": 2
    }
    结果强制#
    确定强制对象的结果是 GraphQL 执行器的核心,因此规范的该部分对此进行了介绍。
    输入强制#
    对象永远不是有效的输入。
    类型验证#
    如果定义不正确,对象类型可能会无效。GraphQL 模式中的每个对象类型都必须遵守这组规则。
    1.
    对象类型必须定义一个或多个字段。
    2.
    对于对象类型的每个字段:
    1.
    该字段在该对象类型中必须具有唯一的名称;任何两个字段不能共享相同的名称。
    2.
    该字段的名称不得以字符“__”(两个下划线)开头。
    3.
    该字段必须返回IsOutputType ( fieldType )返回true 的类型。
    4.
    对于字段的每个参数:
    1.
    参数的名称不得以字符“__”(两个下划线)开头。
    2.
    参数必须接受IsInputType ( argumentType )返回true 的类型。
    3.
    一种对象类型可以声明它实现一个或多个唯一的接口。
    4.
    对象类型必须是它实现的所有接口的超集:
    1.
    令此对象类型为objectType。
    2.
    对于声明为实现为interfaceType 的每个接口,IsValidImplementation ( objectType , interfaceType )必须为 true。
    IsValidImplementation(类型,实现类型):
    1.
    如果ImplementType声明它实现任何接口,则type也必须声明它实现这些接口。
    2.
    type 必须为ImplementType中定义的每个字段包含一个同名的字段。
    a. 令field为type上的命名字段。
    b. 令ImplementedField为ImplementedType上的命名字段。
    c. 对于在 ImplementField中定义的每个参数, field必须包含一个同名的参数。 (字段上的命名参数必须接受与ImplementedField上的命名参数相同的类型(不变)。)
    d. field可以包含在implementedField中未定义的附加参数,但任何附加参数都不是必需的,例如不得为非空类型。
    e. 字段必须返回一个等于ImplementField字段的返回类型的返回类型或其子类型(协变)的类型:
    令fieldType为``field的返回类型。
    令implementedFieldType为implementedField的返回类型。
    IsValidImplementationFieldType ( fieldType , ImplementedFieldType ) 必须为 true。
    IsValidImplementationFieldType(字段类型,实现字段类型):
    1.
    如果fieldType是非空类型:
    1.
    令nullableType为fieldType的展开可为空类型。
    2.
    如果implementedNullableType是 Non-Null 类型,则将 implementedNullableType 设置为implementedFieldType的展开的可空类型,否则直接将其设置为implementedFieldType。
    3.
    返回 IsValidImplementationFieldType ( nullableType , implementedNullableType )。
    2.
    如果fieldType是 List 类型并且implementedFieldType也是 List 类型:
    1.
    令itemType 为fieldType的展开项目类型。
    2.
    令ImplementedItemType为ImplementedFieldType的展开项类型。
    3.
    返回 IsValidImplementationFieldType ( itemType , implementedItemType )。
    3.
    如果fieldType 与ImplementedFieldType 的类型相同,则返回 true。
    4.
    如果fieldType是 Object 类型,implementedFieldType是 Union 类型,并且fieldType 是ImplementedFieldType的可能类型,则返回 true。
    5.
    如果fieldType是对象或接口类型,并且ImplementedFieldType是接口类型,并且fieldType声明它实现ImplementedFieldType,则返回 true。
    6.
    否则返回 false。

    3.6.1 字段参数#

    参数定义:
    (输入值定义列表)
    输入值定义:
    说明(可选)名称: 类型 默认值(可选)指令(常量)(可选)
    对象字段在概念上是产生值的函数。有时,对象字段可以接受参数以进一步指定返回值。对象字段参数定义为所有可能的参数名称及其预期输入类型的列表。
    字段中定义的所有参数不得具有以“__” (两个下划线)开头的名称,因为这是 GraphQL 的自省系统专用的。
    例如,Person带有picture字段的类型可以接受参数来确定要返回的图像大小。
    示例 58type Person {
      name: String
      picture(size: Int): Url
    }
    操作可以选择指定其字段的参数来提供这些参数。
    本例操作:
    示例 59{
      name
      picture(size: 600)
    }
    可能会返回结果:
    示例 60{
      "name": "Mark Zuckerberg",
      "picture": "http://some.cdn/picture_600.jpg"
    }
    对象字段参数的类型必须是输入类型(除对象、接口或联合类型之外的任何类型)。

    3.6.2 现场弃用#

    应用程序认为有必要时,对象中的字段可以标记为已弃用。将这些字段包含在选择集中仍然是合法的(以确保现有客户端不会因更改而中断),但应在文档和工具中适当处理这些字段。
    使用类型系统定义语言时,@deprecated指令用于指示字段已弃用:
    示例 61type ExampleType {
      oldField: String @deprecated
    }

    3.6.3 对象扩展#

    对象类型扩展:
    extend 类型名称 实现接口(可选)指令(常量)(可选)字段定义
    extend 类型名称 实现接口(可选)指令(常量)(可选)
    extend 类型名称 实现接口(可选)
    对象类型扩展用于表示从某些原始类型扩展的类型。例如,这可能用于表示本地数据,或者由 GraphQL 服务来表示,该服务本身就是另一个 GraphQL 服务的扩展。
    在此示例中,本地数据字段被添加到Story类型中:
    示例 62extend type Story {
      isHiddenLocally: Boolean
    }
    对象类型扩展可以选择不添加额外的字段,而只添加接口或指令。
    在此示例中,将指令添加到User类型中而不添加字段:
    示例 63extend type User @addedDirective
    类型验证#
    如果定义不正确,对象类型扩展可能会无效。
    1.
    命名类型必须已定义并且必须是对象类型。
    2.
    对象类型扩展的字段必须具有唯一的名称;任何两个字段不能共享相同的名称。
    3.
    对象类型扩展的任何字段不得已在原始对象类型上定义。
    4.
    提供的任何不可重复指令不得已应用于原始对象类型。
    5.
    提供的任何接口不得已由原始对象类型实现。
    6.
    生成的扩展对象类型必须是它实现的所有接口的超集。

    3.7 接口#

    接口类型定义:
    说明(可选)接口名称 实现接口(可选)指令(常量)(可选)字段定义
    说明(可选)接口名称 实现接口(可选)指令(常量)(可选)
    GraphQL 接口表示命名字段及其参数的列表。然后,GraphQL 对象和接口可以实现这些接口,这要求实现类型将定义这些接口定义的所有字段。
    GraphQL 接口上的字段与 GraphQL 对象上的字段具有相同的规则;它们的类型可以是标量、对象、枚举、接口或联合,或者基本类型是这五种之一的任何包装类型。
    例如,接口NamedEntity可以描述所需的字段和类型,例如Person,或者Business然后可以实现该接口以保证该字段将始终存在。
    类型还可以实现多个接口。例如,Business实现以下示例中的NamedEntity和接口。ValuedEntity
    示例 64interface NamedEntity {
      name: String
    }
    
    interface ValuedEntity {
      value: Int
    }
    
    type Person implements NamedEntity {
      name: String
      age: Int
    }
    
    type Business implements NamedEntity & ValuedEntity {
      name: String
      value: Int
      employeeCount: Int
    }
    当需要多种对象类型之一时,产生接口的字段非常有用,但应该保证某些字段。
    为了继续这个例子, aContact可能指的是NamedEntity。
    示例 65type Contact {
      entity: NamedEntity
      phoneNumber: String
      address: String
    }
    这允许我们编写一个Contact可以选择公共字段的选择集。
    示例 66{
      entity {
        name
      }
      phoneNumber
    }
    选择接口类型上的字段时,只能查询接口上声明的字段。在上面的示例中,entity返回 a NamedEntity,并且name是在 上定义的NamedEntity,因此它是有效的。但是,以下内容不是针对 的有效选择集Contact:
    反例第 67 号{
      entity {
        name
        age
      }
      phoneNumber
    }
    因为entity引用了NamedEntity, 并且age未在该接口上定义。仅当 的结果为时,查询age才有效;这可以使用片段或内联片段来表达:entity``Person
    示例 68{
      entity {
        name
        ... on Person {
          age
        }
      }
      phoneNumber
    }
    接口实现接口#
    当定义一个实现另一个接口的接口时,实现接口必须定义所实现的接口指定的每个字段。例如,Resource 接口必须定义字段 id 才能实现 Node 接口:
    示例 69interface Node {
      id: ID!
    }
    
    interface Resource implements Node {
      id: ID!
      url: String
    }
    传递实现的接口(由正在实现的接口实现的接口)也必须在实现类型或接口上定义。例如,如果不同时实现Image就无法实现:Resource``Node
    示例 70interface Node {
      id: ID!
    }
    
    interface Resource implements Node {
      id: ID!
      url: String
    }
    
    interface Image implements Resource & Node {
      id: ID!
      url: String
      thumbnail: String
    }
    接口定义不得包含循环引用,也不得实现自身。这个例子是无效的,因为Node和Named实现它们自己和彼此:
    反例#71interface Node implements Named & Node {
      id: ID!
      name: String
    }
    
    interface Named implements Node & Named {
      id: ID!
      name: String
    }
    结果强制#
    接口类型应该有某种方法来确定给定结果对应于哪个对象。一旦完成,接口的结果强制转换与对象的结果强制转换相同。
    输入强制#
    接口永远不是有效的输入。
    类型验证#
    如果定义不正确,接口类型可能会无效。
    1.
    接口类型必须定义一个或多个字段。
    2.
    对于接口类型的每个字段:
    1.
    该字段在该接口类型中必须具有唯一的名称;任何两个字段不能共享相同的名称。
    2.
    该字段的名称不得以字符“__”(两个下划线)开头。
    3.
    该字段必须返回 IsOutputType ( fieldType )返回true 的类型。
    4.
    对于字段的每个参数:
    1.
    参数的名称不得以字符“__”(两个下划线)开头。
    2.
    参数必须接受 IsInputType ( argumentType )返回 true 的类型。
    3.
    接口类型可以声明它实现一个或多个唯一接口,但可能不实现自身。
    4.
    接口类型必须是它实现的所有接口的超集:
    1.
    令此接口类型为implementingType。
    2.
    对于声明为实现的每个接口,IsValidImplementation ( implementType , implementedType )必须为true。

    3.7.1 接口扩展#

    InterfaceTypeExtension:
    extend 接口名称 实现的接口(可选) 指令(常量可选) 字段定义
    extend 接口名称 实现的接口(可选) 指令(常量可选)
    extend 接口名称 实现的接口(可选)
    接口类型扩展用于表示从某个原始接口扩展而来的接口。例如,这可能用于表示多种类型的常见本地数据,或者由本身是另一个 GraphQL 服务扩展的 GraphQL 服务使用。
    NamedEntity在此示例中,扩展数据字段与实现它的类型一起添加到类型中:
    示例 72extend interface NamedEntity {
      nickname: String
    }
    
    extend type Person {
      nickname: String
    }
    
    extend type Business {
      nickname: String
    }
    接口类型扩展可以选择不添加额外的字段,而只添加指令。
    在此示例中,将指令添加到NamedEntity类型中而不添加字段:
    示例 73extend interface NamedEntity @addedDirective
    类型验证#
    如果定义不正确,接口类型扩展可能会无效。
    1.
    命名类型必须已定义并且必须是接口类型。
    2.
    接口类型扩展的字段必须具有唯一的名称;任何两个字段不能共享相同的名称。
    3.
    接口类型扩展的任何字段不得已在原始接口类型上定义。
    4.
    实现原始接口类型的任何对象或接口类型也必须是接口类型扩展的字段的超集(这可能是由于对象类型扩展)。
    5.
    提供的任何不可重复指令不得已应用于原始接口类型。
    6.
    生成的扩展接口类型必须是它实现的所有接口的超集。

    3.8 联合类型#

    UnionTypeDefinition:
    描述(可选) 联合类型名称 指令(可选) = 联合成员类型(可选)
    联合成员类型:
    联合成员类型 | 已命名类型
    = | (可选)已命名类型
    GraphQL 联合表示一个对象,可以是 GraphQL 对象类型列表之一,但不提供这些类型之间的保证字段。它们与接口的不同之处还在于,对象类型声明它们实现的接口,但不知道哪些联合包含它们。
    对于接口和对象,只能直接查询那些定义在类型上的字段;要查询接口上的其他字段,必须使用类型化片段。这与联合相同,但联合不定义任何字段,因此如果不使用类型精炼片段或内联片段(元字段__typename除外),则不能在此类型上查询任何字段。
    例如,我们可以定义以下类型:
    示例 74union SearchResult = Photo | Person
    
    type Person {
      name: String
      age: Int
    }
    
    type Photo {
      height: Int
      width: Int
    }
    
    type SearchQuery {
      firstSearchResult: SearchResult
    }
    在此示例中,如果结果是“人”,则查询操作需要名称;如果结果是照片,则需要高度。但是,由于联合本身没有定义字段,因此这可能不明确且无效。
    反例#75{
      firstSearchResult {
        name
        height
      }
    }
    有效的操作包括类型化片段(在本例中为内联片段):
    示例 76{
      firstSearchResult {
        ... on Person {
          name
        }
        ... on Photo {
          height
        }
      }
    }
    联合成员可以使用可选的前导|字符来定义,以在表示较长的可能类型列表时帮助格式化:
    示例 77union SearchResult =
      | Photo
      | Person
    结果强制#
    联合类型应该有某种方法来确定给定结果对应于哪个对象。一旦这样做,联合的结果强制转换与对象的结果强制转换相同。
    输入强制#
    联合永远不是有效的输入。
    类型验证#
    如果定义不正确,联合类型可能会无效。
    1.
    联合类型必须包含一个或多个唯一的成员类型。
    2.
    Union 类型的成员类型必须全部是 Object 基类型;标量、接口和联合类型不得是联合的成员类型。同样,包装类型不能是 Union 的成员类型。

    3.8.1 联合类型扩展#

    UnionTypeExtension:
    extendunion 名称指令常量(可选)UnionMemberTypes
    extendunion 名称指令常量
    联合类型扩展用于表示从某些原始联合类型扩展而来的联合类型。例如,这可能用于表示其他本地数据,或者由本身是另一个 GraphQL 服务扩展的 GraphQL 服务使用。
    类型验证#
    如果定义不正确,联合类型扩展可能会无效。
    1.
    命名类型必须已定义并且必须是 Union 类型。
    2.
    Union 类型扩展的成员类型必须全部是 Object 基类型;标量、接口和联合类型不得是联合的成员类型。同样,包装类型不能是 Union 的成员类型。
    3.
    联合类型扩展的所有成员类型必须是唯一的。
    4.
    Union 类型扩展的所有成员类型不得都是原始 Union 类型的成员。
    5.
    提供的任何不可重复指令不得已应用于原始 Union 类型。

    3.9 枚举#

    枚举类型定义:
    说明 (可选) 枚举名称 指令 (常量) 可选 枚举值定义
    枚举值定义:
    { 枚举值定义列表 }
    枚举值定义:
    说明 (可选) 枚举值 指令 (常量) 可选
    GraphQL 枚举类型与标量类型一样,也表示 GraphQL 类型系统中的叶值。然而,枚举类型描述了可能值的集合。
    枚举不是数值的引用,而是其本身的唯一值。它们可以序列化为字符串:表示值的名称。
    在此示例中,Direction定义了一个名为的 Enum 类型:
    示例 78enum Direction {
      NORTH
      EAST
      SOUTH
      WEST
    }
    结果强制#
    GraphQL 服务必须返回定义的一组可能值之一。如果不可能进行合理的强制,他们必须提出现场错误。
    输入强制#
    GraphQL 有一个常量文字来表示枚举输入值。GraphQL 字符串文字不得作为枚举输入接受,而是引发请求错误。
    对非字符串符号值(例如 EDN)具有不同表示形式的变量传输序列化应仅允许此类值作为枚举输入值。否则,对于大多数不支持的传输序列化,字符串可能会被解释为具有相同名称的枚举输入值。
    类型验证#
    如果定义不正确,枚举类型可能会无效。
    1.
    枚举类型必须定义一个或多个唯一的枚举值。

    3.9.1 枚举类型扩展#

    EnumTypeExtension:
    extend enum Name DirectivesConstopt EnumValuesDefinition
    extend enum Name DirectivesConst
    枚举类型扩展用于表示从某些原始枚举类型扩展而来的枚举类型。例如,这可能用于表示其他本地数据,或者由本身是另一个 GraphQL 服务扩展的 GraphQL 服务使用。
    类型验证#
    如果定义不正确,枚举类型扩展可能会无效。
    1.
    命名类型必须已定义并且必须是 Enum 类型。
    2.
    Enum 类型扩展的所有值都必须是唯一的。
    3.
    Enum 类型扩展的所有值都不能是原始 Enum 的值。
    4.
    提供的任何不可重复指令不得已应用于原始 Enum 类型。

    3.10 输入对象#

    InputObjectTypeDefinition:
    描述(可选) 输入对象名 指令(常量,可选) 输入字段定义
    描述(可选) 输入对象名 指令(常量,可选)
    InputFieldsDefinition:
    {InputValueDefinition 列表}
    字段可以接受参数来配置其行为。这些输入通常是标量或枚举,但有时需要表示更复杂的值。
    GraphQL 输入对象定义了一组输入字段;输入字段可以是标量、枚举或其他输入对象。这允许参数接受任意复杂的结构。
    Point2D在此示例中,称为描述x和输入的输入对象y:
    示例 79input Point2D {
      x: Float
      y: Float
    }
    注意:上面定义的GraphQL 对象类型 ( ObjectTypeDefinition ) 不适合在此处重用,因为对象类型可以包含定义参数的字段或包含对接口和联合的引用,而这两种类型都不适合用作输入参数。因此,输入对象在系统中具有单独的类型。
    循环引用#
    输入对象可以引用其他输入对象作为字段类型。当输入对象直接或通过引用的输入对象引用自身时,就会发生循环引用。
    通常允许循环引用,但它们可能不会被定义为非空奇异字段的不间断链。此类输入对象是无效的,因为无法为它们提供合法值。
    此循环引用输入类型的示例是有效的,因为可以省略字段self或值为null。
    示例#80input Example {
      self: Example
      value: String
    }
    该示例也是有效的,因为该字段self可能是空列表。
    示例#81input Example {
      self: [Example!]!
      value: String
    }
    此循环引用输入类型的示例无效,因为self无法为该字段提供有限值。
    反例#82input Example {
      value: String
      self: Example!
    }
    First.second此示例也是无效的,因为通过和字段存在非空单数循环引用Second.first。
    反例#83input First {
      second: Second!
      value: String
    }
    
    input Second {
      first: First!
      value: String
    }
    结果强制#
    输入对象永远不是有效的结果。输入对象类型不能是对象或接口字段的返回类型。
    输入强制#
    输入对象的值应该是输入对象文字或变量提供的无序映射,否则必须引发请求错误。在任何一种情况下,输入对象文字或无序映射都不得包含任何名称未由该输入对象类型的字段定义的条目,否则必须引发响应错误。
    强制转换的结果是一个无序映射,其中每个字段都有一个条目,该条目由输入对象类型定义并且存在值。生成的地图按照以下规则构建:
    如果没有为定义的输入对象字段提供值,并且该字段定义提供了默认值,则应使用默认值。如果未提供默认值并且输入对象字段的类型为非空,则应引发错误。否则,如果不需要该字段,则不会将任何条目添加到强制无序映射中。
    如果为输入对象字段提供了值null,并且该字段的类型不是非 null 类型,则强制无序映射中的条目将被赋予值null。换句话说,显式提供的值null与未提供值之间存在语义差异。
    如果为输入对象字段提供了文字值,则强制无序映射中的条目将被赋予根据该字段类型的输入强制规则强制该值的结果。
    如果为输入对象字段提供变量,则必须使用该变量的运行时值。如果运行时值为null并且字段类型为非 null,则必须引发字段错误。如果未提供运行时值,则应使用变量定义的默认值。如果变量定义未提供默认值,则应使用输入对象字段定义的默认值。
    String以下是具有字段a和必填(非空)Int!字段的输入对象类型的输入强制示例b:
    示例#84input ExampleInputObject {
      a: String
      b: Int!
    }
    字面值变量强制价值
    { a: "abc", b: 123 }{}{ a: "abc", b: 123 }
    { a: null, b: 123 }{}{ a: null, b: 123 }
    { b: 123 }{}{ b: 123 }
    { a: $var, b: 123 }{ var: null }{ a: null, b: 123 }
    { a: $var, b: 123 }{}{ b: 123 }
    { b: $var }{ var: 123 }{ b: 123 }
    $var{ var: { b: 123 } }{ b: 123 }
    "abc123"{}错误:值不正确
    $var{ var: "abc123" }错误:值不正确
    { a: "abc", b: "123" }{}错误:字段b的值不正确``
    { a: "abc" }{}错误:缺少必填字段b
    { b: $var }{}错误:缺少必填字段b。
    $var{ var: { a: "abc" } }错误:缺少必填字段b
    { a: "abc", b: null }{}错误:b必须为非空。
    { b: $var }{ var: null }错误:b必须为非空。
    { b: 123, c: "xyz" }{}错误:意外的字段c
    类型验证#
    1.
    输入对象类型必须定义一个或多个输入字段。
    2.
    对于输入对象类型的每个输入字段:
    1.
    输入字段在该输入对象类型中必须具有唯一的名称;两个输入字段不能共享相同的名称。
    2.
    输入字段的名称不得以字符“__”(两个下划线)开头。
    3.
    输入字段必须接受 IsInputType ( inputFieldType )返回 true 的类型。
    3.
    如果输入对象直接引用自身或通过引用的输入对象引用自身,则引用链中的至少一个字段必须是可空类型或列表类型。

    3.10.1 输入对象扩展#

    InputObjectTypeExtension:
    extend 输入对象名 指令(常量,可选) 输入字段定义
    extend 输入对象名 指令(常量,可选)
    输入对象类型扩展用于表示从某些原始输入对象类型扩展的输入对象类型。例如,这可能由 GraphQL 服务使用,该服务本身就是另一个 GraphQL 服务的扩展。
    类型验证#
    如果定义不正确,输入对象类型扩展可能会无效。
    1.
    命名类型必须已定义并且必须是输入对象类型。
    2.
    输入对象类型扩展的所有字段都必须具有唯一的名称。
    3.
    输入对象类型扩展的所有字段不得都是原始输入对象的字段。
    4.
    提供的任何不可重复指令不得已应用于原始输入对象类型。

    3.11 列表#

    GraphQL 列表是一种特殊的集合类型,它声明列表中每个项目的类型(称为列表的项目类型)。 列表值被序列化为有序列表,其中列表中的每个项目都根据项目类型进行序列化。
    为了表示字段使用 List 类型,项目类型用方括号括起来,如下所示:pets: [Pet]。允许嵌套列表:matrix: [[Int]].
    结果强制#
    GraphQL 服务必须返回一个有序列表作为列表类型的结果。列表中的每个项目必须是项目类型结果强制的结果。如果不可能进行合理的强制,则必须引发字段错误。特别是,如果返回非列表,则强制转换应该失败,因为这表明类型系统和实现之间的期望不匹配。
    如果列表的项目类型可为空,则在准备或强制列表中单个项目期间发生的错误必须导致列表中该位置处的值为null以及添加到响应中的字段错误。如果列表的项目类型为非空,则列表中单个项目发生的字段错误必定会导致整个列表出现字段错误。
    注意:有关此行为的更多信息, 请参阅处理字段错误。
    输入强制#
    当预期作为输入时,仅当列表中的每个项目可以被列表的项目类型接受时,列表值才会被接受。
    如果作为输入传递给列表类型的值不是列表也不是空值,则输入强制转换的结果是大小为 1 的列表,其中单个项值是列表项类型的输入强制转换的结果提供的值(请注意,这可能递归地应用于嵌套列表)。
    这允许接受一个或多个参数(有时称为“var args”)的输入将其输入类型声明为列表,而对于单个值的常见情况,客户端可以直接传递该值,而不是构造列表。
    以下是具有各种列表类型和值的输入强制的示例:
    预期类型提供的价值强制价值
    [Int][1, 2, 3][1, 2, 3]
    [Int][1, "b", true]错误:项目值不正确
    [Int]1[1]
    [Int]nullnull
    [[Int]][[1], [2, 3]][[1], [2, 3]]
    [[Int]][1, 2, 3]错误:项目值不正确
    [[Int]]1[[1]]
    [[Int]]nullnull

    3.12 Non-Null#

    默认情况下,GraphQL 中的所有类型都可以为 null;null 值是上述所有类型的有效响应。要声明不允许 null 的类型,可以使用 GraphQL Non-Null 类型。此类型包装基础类型,并且此类型的行为与包装类型相同,但 null 不是包装类型的有效响应。尾随感叹号用于表示使用非空类型的字段,如下所示:name: String!。
    可空与可选#
    在选择集的上下文中,字段始终是可选的,可以省略字段,并且选择集仍然有效。 但是,返回非 Null 类型的字段在查询时绝不会返回null值。
    默认情况下,输入(例如字段参数)始终是可选的。但是,需要非空输入类型。除了不接受null值之外,它也不接受省略。为了简单起见,可空类型始终是可选的,非空类型始终是必需的。
    结果强制#
    在所有上述结果强制转换中,null被视为有效值。要强制非空类型的结果,应执行包装类型的强制。如果该结果不为null,则强制转换 Non-Null 类型的结果就是该结果。如果该结果为null,则必须引发字段错误。
    注意:当非空值引发字段错误时,该错误将传播到父字段。有关此过程的更多信息,请参阅执行部分中的“错误和不可空性”。
    输入强制#
    如果未提供非 Null 类型的参数或输入对象字段,提供了文字值 null,或者提供了在运行时未提供值或提供了值 null 的变量,则必须提出请求错误。
    如果提供给 Non-Null 类型的值提供了 null 以外的文字值或 Non-Null 变量值,则使用包装类型的输入强制对其进行强制。
    不能省略非空参数:
    反例#85{
      fieldWithNonNullArg
    }
    不能将null值提供给非 null 参数:
    反例#86{
      fieldWithNonNullArg(nonNullArg: null)
    }
    不能将可空类型的变量提供给非空参数:
    示例#87query withNullableVariable($var: String) {
      fieldWithNonNullArg(nonNullArg: $var)
    }
    注意:验证部分将向非空输入类型提供可为空变量类型定义为无效。
    类型验证#
    1.
    非空类型不得包装另一个非空类型。

    3.12.1 组合列表和非空类型#

    List 和 Non-Null 包装类型可以组合,表示更复杂的类型。列表和非空类型的结果强制和输入强制的规则以递归方式应用。
    例如,如果 List 的内部项类型为 Non-Null(例如[T!]),则该 List 可能不包含任何null项。但是,如果 Non-Null 的内部类型是 List(例如[T]!),则不接受null ,但接受空列表。
    以下是具有各种类型和值的结果强制转换的示例:
    预期类型内在价值强制结果
    [Int][1, 2, 3][1, 2, 3]
    [Int]nullnull
    [Int][1, 2, null][1, 2, null]
    [Int][1, 2, Error][1, 2, null](已记录错误)
    [Int]![1, 2, 3][1, 2, 3]
    [Int]!null错误:值不能为空
    [Int]![1, 2, null][1, 2, null]
    [Int]![1, 2, Error][1, 2, null](已记录错误)
    [Int!][1, 2, 3][1, 2, 3]
    [Int!]nullnull
    [Int!][1, 2, null]null(记录了强制错误)
    [Int!][1, 2, Error]null(已记录错误)
    [Int!]![1, 2, 3][1, 2, 3]
    [Int!]!null错误:值不能为空
    [Int!]![1, 2, null]错误:项目不能为空
    [Int!]![1, 2, Error]错误:项目中发生错误

    3.13 指令#

    指令定义:
    (可选)描述指令@名称参数定义(可选)可重复(可选)适用于指令位置
    指令地点:
    指令位置|指令位置(可选)指令位置
    | 选择指令位置
    指令位置:
    可执行指令位置
    类型系统指令位置
    可执行指令位置:
    QUERY
    MUTATION
    SUBSCRIPTION
    FIELD
    FRAGMENT_DEFINITION
    FRAGMENT_SPREAD
    INLINE_FRAGMENT
    VARIABLE_DEFINITION
    类型系统指令位置:
    模式
    SCALAR
    OBJECT
    FIELD_DEFINITION
    ARGUMENT_DEFINITION
    INTERFACE
    UNION
    ENUM
    ENUM_VALUE
    INPUT_OBJECT
    INPUT_FIELD_DEFINITION
    GraphQL 模式描述了用于注释 GraphQL 文档各个部分的指令,作为指示符,指示验证器、执行器或客户端工具(例如代码生成器)应对它们进行不同的评估。
    内置指令#
    内置指令是本规范中定义的任何指令。
    GraphQL 实现应提供@skip和@include指令。
    如果表示模式中的已弃用部分,GraphQL 实现支持类型系统定义语言,必须提供 @deprecated 指令。
    如果表示自定义标量定义,支持类型系统定义语言的 GraphQL 实现应该提供该@specifiedBy指令。
    当使用类型系统定义语言表示 GraphQL 模式时,为了简洁起见,可以省略任何内置指令。
    在调查 GraphQL 服务时,所有提供的指令,包括任何内建指令,必须包含在返回的指令集中。
    定制指令#
    GraphQL 服务和客户端工具可能会提供本文档中定义之外的任何其他自定义指令。指令是使用自定义或实验行为扩展 GraphQL 的首选方式。
    注意:定义自定义指令 时,建议在指令名称前加上前缀,以明确其使用范围,并防止与本文档的未来版本可能指定的内置指令_(将不包含在其名称中)发生冲突。例如, Facebook 的 GraphQL 服务使用的自定义指令应命名@fb_auth为@auth. 特别建议对本规范提出的补充,这些补充可能会在RFC 过程中发生变化。例如,正在进行的版本@live应命名为@rfc_live.
    指令只能在它们声明所属的位置使用。在此示例中,定义了一个可用于注释字段的指令:
    示例#88directive @example on FIELD
    
    fragment SomeFragment on SomeType {
      field @example
    }
    指令位置可以使用可选的前导字符来定义,|以在表示较长的可能位置列表时帮助格式化:
    示例#89directive @example on
      | FIELD
      | FRAGMENT_SPREAD
      | INLINE_FRAGMENT
    指令还可以用于注释类型系统定义语言,这可以成为提供附加元数据的有用工具,以便生成 GraphQL 执行服务、生成客户端生成的运行时代码或 GraphQL 语义的许多其他有用扩展。
    在此示例中,指令@example注释字段和参数定义:
    示例 90directive @example on FIELD_DEFINITION | ARGUMENT_DEFINITION
    
    type SomeType {
      field(arg: Int @example): String @example
    }
    可以通过包含“repeatable”关键字将指令定义为可重复的。当同一指令应在单个位置与不同参数一起使用时,可重复指令通常很有用,特别是在需要通过指令向类型或架构扩展提供附加信息的情况下:
    示例 91directive @delegateField(name: String!) repeatable on OBJECT | INTERFACE
    
    type Book @delegateField(name: "pageCount") @delegateField(name: "author") {
      id: ID!
    }
    
    extend type Book @delegateField(name: "index")
    定义指令时,不得直接或间接引用自身:
    反例第 92 号directive @invalidExample(arg: String @invalidExample) on ARGUMENT_DEFINITION
    注意:指令出现的顺序可能很重要,包括可重复的指令。
    验证#
    1.
    指令定义不得包含直接引用自身的指令的使用。
    2.
    指令定义不得包含通过引用类型或指令来间接引用自身的指令的使用,该类型或指令可传递地包含对此指令的引用。
    3.
    该指令的名称不得以字符“__”(两个下划线)开头。
    4.
    对于指令的每个参数:
    1.
    参数的名称不得以字符“__”(两个下划线)开头。
    2.
    参数必须接受 IsInputType ( argumentType )返回true 的类型。

    3.13.1 @skip#

    directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
    可以为字段、片段扩展和内联片段提供@skip 内置指令,并允许在执行期间进行条件排除,如if参数所描述的。
    在此示例中,experimentalField仅当变量$someTest具有值时才会被查询false。
    示例 93query myQuery($someTest: Boolean!) {
      experimentalField @skip(if: $someTest)
    }

    3.13.2 @include#

    directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
    内建指令 @include 可以应用于字段、片段扩展和内联片段,它允许在执行过程中根据 if 参数的条件进行条件包含。
    在这个例子中,仅当变量 $someTest 的值为 true 时,experimentalField 才会被查询。
    示例 94query myQuery($someTest: Boolean!) {
      experimentalField @include(if: $someTest)
    }
    注意:在 GraphQL 中,@skip 和 @include 指令并没有优先级的区别。如果在同一个字段或者片段上同时使用了 @skip 和 @include 指令,只有当 @skip 的条件为 false 而且 @include 的条件为 true 时,该字段或片段才会被查询到。反过来说,如果 @skip 的条件为 true 或者 @include 的条件为 false,则该字段或片段不会被查询到。

    3.13.3 @deprecated#

    directive @deprecated(
      reason: String = "No longer supported"
    ) on FIELD_DEFINITION | ENUM_VALUE
    @deprecated 内置指令在类型系统定义语言中用于指示 GraphQL 服务架构中已弃用的部分,例如类型上已弃用的字段或已弃用的枚举值。
    弃用包括弃用的原因,该原因使用 Markdown 语法(如 CommonMark 指定)进行格式化。
    在此示例中,oldField不推荐使用类型定义,而改用newField.
    示例 95type ExampleType {
      newField: String
      oldField: String @deprecated(reason: "Use `newField`.")
    }

    3.13.4 @specifiedBy#

    directive @specifiedBy(url: String!) on SCALAR
    内置@specifiedBy 指令在类型系统定义语言中使用,以提供标量规范 URL,用于指定自定义标量类型的行为。URL 应指向人类可读的数据格式、序列化和强制规则规范。它不得出现在内置标量类型上。
    UUID在此示例中,使用指向相关 IETF 规范的 URL 定义了 的自定义标量类型。
    示例 96scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")

    4 内省#

    GraphQL 服务支持对其架构进行内省。使用 GraphQL 本身查询该模式,为工具构建创建了一个强大的平台。
    以一个简单应用程序的请求为例。在本例中,有一个包含三个字段的 User 类型:id、name 和birthday。
    例如,给定具有以下类型定义的服务:
    示例 97type User {
      id: String
      name: String
      birthday: Date
    }
    包含操作的请求:
    示例 98{
      __type(name: "User") {
        name
        fields {
          name
          type {
            name
          }
        }
      }
    }
    会产生结果:
    示例 99{
      "__type": {
        "name": "User",
        "fields": [
          {
            "name": "id",
            "type": { "name": "String" }
          },
          {
            "name": "name",
            "type": { "name": "String" }
          },
          {
            "name": "birthday",
            "type": { "name": "Date" }
          }
        ]
      }
    }
    保留名称
    GraphQL 自省系统所需的类型和字段与用户定义的类型和字段在相同的上下文中使用,其前缀为“__”两个下划线。这是为了避免与用户定义的 GraphQL 类型发生命名冲突。
    否则,GraphQL 类型系统中的任何名称都不能以两个下划线"__"开头。

    4.1 类型名称内省#

    GraphQL 支持操作中任何选择集中的类型名称内省,但订阅操作根部的选择除外。__typename: String!类型名称自省是通过任何对象、接口或联合上的元字段来完成的。它返回执行期间此时具体对象类型的名称。
    在查询 Interface 或 Union 类型以确定已返回可能类型中的实际对象类型时,最常使用此方法。
    作为元字段,__typename它是隐式的,不会出现在任何定义类型的字段列表中。
    笔记__typename不能作为根字段包含在订阅操作中。

    4.2 模式内省#

    模式内省系统可从元字段访问__schema,并且__type可从查询操作的根的类型访问。
    __schema: __Schema!
    __type(name: String!): __Type
    与所有元字段一样,这些元字段是隐式的,不会出现在查询操作的根类型的字段列表中。
    一流的文档#
    内省系统中的所有类型都提供了description类型字段String,以允许类型设计者除了功能之外还可以发布文档。GraphQL 服务可以使用 Markdown 语法(由CommonMarkdescription指定)返回字段。因此,建议任何显示工具都使用兼容 CommonMark 的 Markdown 渲染器。description
    弃用#
    为了支持向后兼容性的管理,GraphQL 字段和枚举值可以指示它们是否已弃用 ( isDeprecated: Boolean) 以及弃用原因的描述 ( deprecationReason: String)。
    使用 GraphQL 自省构建的工具应该尊重弃用,通过信息隐藏或面向开发人员的警告来阻止弃用的使用。
    架构自省架构#
    模式自省系统本身表示为 GraphQL 模式。以下是提供模式自省的全套类型系统定义,这些定义在以下各节中进行了完整定义。
    type __Schema {
      description: String
      types: [__Type!]!
      queryType: __Type!
      mutationType: __Type
      subscriptionType: __Type
      directives: [__Directive!]!
    }
    
    type __Type {
      kind: __TypeKind!
      name: String
      description: String
      # must be non-null for OBJECT and INTERFACE, otherwise null.
      fields(includeDeprecated: Boolean = false): [__Field!]
      # must be non-null for OBJECT and INTERFACE, otherwise null.
      interfaces: [__Type!]
      # must be non-null for INTERFACE and UNION, otherwise null.
      possibleTypes: [__Type!]
      # must be non-null for ENUM, otherwise null.
      enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
      # must be non-null for INPUT_OBJECT, otherwise null.
      inputFields: [__InputValue!]
      # must be non-null for NON_NULL and LIST, otherwise null.
      ofType: __Type
      # may be non-null for custom SCALAR, otherwise null.
      specifiedByURL: String
    }
    
    enum __TypeKind {
      SCALAR
      OBJECT
      INTERFACE
      UNION
      ENUM
      INPUT_OBJECT
      LIST
      NON_NULL
    }
    
    type __Field {
      name: String!
      description: String
      args: [__InputValue!]!
      type: __Type!
      isDeprecated: Boolean!
      deprecationReason: String
    }
    
    type __InputValue {
      name: String!
      description: String
      type: __Type!
      defaultValue: String
    }
    
    type __EnumValue {
      name: String!
      description: String
      isDeprecated: Boolean!
      deprecationReason: String
    }
    
    type __Directive {
      name: String!
      description: String
      locations: [__DirectiveLocation!]!
      args: [__InputValue!]!
      isRepeatable: Boolean!
    }
    
    enum __DirectiveLocation {
      QUERY
      MUTATION
      SUBSCRIPTION
      FIELD
      FRAGMENT_DEFINITION
      FRAGMENT_SPREAD
      INLINE_FRAGMENT
      VARIABLE_DEFINITION
      SCHEMA
      SCALAR
      OBJECT
      FIELD_DEFINITION
      ARGUMENT_DEFINITION
      INTERFACE
      UNION
      ENUM
      ENUM_VALUE
      INPUT_OBJECT
      INPUT_FIELD_DEFINITION
    }

    4.2.1__Schema 类型#

    该__Schema类型从元字段返回__schema,并提供有关 GraphQL 服务架构的所有信息。
    领域:
    description可能返回 String 或null。
    queryType是查询操作的根类型。
    mutationType是突变操作的根类型(如果支持)。否则为null。
    subscriptionType是订阅操作的根类型(如果支持)。否则为null。
    types必须返回此模式中包含的所有命名类型的集合。可以通过任何内省类型的字段找到的任何命名类型都必须包含在该集合中。
    directives必须返回此模式中可用的所有指令的集合,包括所有内置指令。

    4.2.2__Type 类型#

    __Type是类型自省系统的核心,它代表系统中的所有类型:命名类型(例如标量和对象类型)和类型修饰符(例如列表和非空类型)。
    类型修饰符用于修改字段中呈现的类型ofType。该修改类型可以递归地成为修改类型,表示列表、不可空值及其组合,最终修改命名类型。
    有几种不同的类型。在每种类型中,不同的字段实际上是有效的。所有可能的类型都列在枚举中__TypeKind。
    下面的每个小节定义了__Type给定枚举的每个可能值的预期字段__TypeKind:
    "SCALAR"
    "OBJECT"
    "INTERFACE"
    "UNION"
    "ENUM"
    "INPUT_OBJECT"
    "LIST"
    "NON_NULL"
    标量#
    表示标量类型,例如 Int、String 和 Boolean。标量不能有字段。
    还表示可以作为标量规范 URL提供的自定义标量。specifiedByURL
    领域:
    kind必须返回__TypeKind.SCALAR。
    name必须返回一个字符串。
    description可能返回 String 或null。
    specifiedByURL可以为自定义标量返回一个 String (以 URL 的形式),否则必须为null。
    所有其他字段必须返回null。
    目的#
    对象类型表示字段集的具体实例。内省类型(例如__Type、__Field等)是对象的示例。
    领域:
    kind必须返回__TypeKind.OBJECT。
    name必须返回一个字符串。
    description可能返回 String 或null。
    fields必须返回可以为此类型选择的字段集。
    includeDeprecated接受默认为false 的参数。如果为true,则还会返回已弃用的字段。
    interfaces必须返回对象实现的接口集(如果没有,则interfaces必须返回空集)。
    所有其他字段必须返回null。
    联盟#
    联合是一种抽象类型,没有声明公共字段。联合的可能类型在 中明确列出possibleTypes。类型可以成为联合的一部分,而无需修改该类型。
    领域:
    kind必须返回__TypeKind.UNION。
    name必须返回一个字符串。
    description可能返回 String 或null。
    possibleTypes返回可以在此联合中表示的类型列表。它们必须是对象类型。
    所有其他字段必须返回null。
    界面#
    接口是一种抽象类型,其中声明了公共字段。任何实现接口的类型都必须定义名称和类型完全匹配的所有字段。该接口的实现在 中明确列出possibleTypes。
    领域:
    kind必须返回__TypeKind.INTERFACE。
    name必须返回一个字符串。
    description可能返回 String 或null。
    fields必须返回该接口所需的字段集。
    includeDeprecated接受默认为false 的参数。如果为true,则还会返回已弃用的字段。
    interfaces必须返回对象实现的接口集(如果没有,则interfaces必须返回空集)。
    possibleTypes返回实现此接口的类型列表。它们必须是对象类型。
    所有其他字段必须返回null。
    枚举#
    枚举是特殊的标量,只能具有一组定义的值。
    领域:
    kind必须返回__TypeKind.ENUM。
    name必须返回一个字符串。
    description可能返回 String 或null。
    enumValues必须以 的列表形式返回枚举值集__EnumValue。必须至少有一个,并且它们必须具有唯一的名称。
    includeDeprecated接受默认为false 的参数。如果为true,则还会返回已弃用的枚举值。
    所有其他字段必须返回null。
    输入对象#
    输入对象是定义为命名输入值列表的复合类型。它们仅用作参数和变量的输入,不能是字段返回类型。
    例如,输入对象Point可以定义为:
    示例 100input Point {
      x: Int
      y: Int
    }
    领域:
    kind必须返回__TypeKind.INPUT_OBJECT。
    name必须返回一个字符串。
    description可能返回 String 或null。
    inputFields必须以 的列表形式返回输入字段集__InputValue。
    所有其他字段必须返回null。
    列表#
    列表表示 GraphQL 中的值序列。列表类型是一个类型修饰符:它在字段中包装另一个类型实例ofType,该实例定义列表中每个项目的类型。
    字段中的修改类型ofType本身可以是修改类型,允许表示列表的列表或非空列表。
    领域:
    kind必须返回__TypeKind.LIST。
    ofType必须返回任何类型。
    所有其他字段必须返回null。
    非空#
    GraphQL 类型可为空。值null是字段类型的有效响应。
    非空类型是类型修饰符:它在ofType字段中包装另一个类型实例。非 null 类型不允许null作为响应,并指示参数和输入对象字段所需的输入。
    该ofType字段中的修改类型本身可以是修改的列表类型,允许表示非空列表。但是,它不能是修改的 Non-Null 类型,以避免出现冗余的 Non-Null 的 Non-Null。
    领域:
    kind必须返回__TypeKind.NON_NULL。
    ofType必须返回除非空之外的任何类型。
    所有其他字段必须返回null。

    4.2.3__Field 类型#

    该__Field类型表示对象或接口类型中的每个字段。
    领域:
    name必须返回一个字符串
    description可能返回 String 或 null
    args``__InputValue返回表示该字段接受的参数的列表。
    type必须返回一个__Type表示该字段返回值的类型。
    isDeprecated如果不应再使用此字段,则返回 true ,否则返回 false。
    deprecationReason(可选)提供不推荐使用该字段的原因。

    4.2.4__InputValue 类型#

    该__InputValue类型表示字段和指令参数以及inputFields输入对象的类型。
    领域:
    name必须返回一个字符串
    description可能返回 String 或 null
    type必须返回__Type表示此输入值期望的类型。
    defaultValue在运行时未提供值的情况下,可能会返回此输入值使用的默认值的字符串编码(使用 GraphQL 语言)。如果此输入值没有默认值,则返回 null。

    4.2.5__EnumValue 类型#

    该__EnumValue类型表示枚举的可能值之一。
    领域:
    name必须返回一个字符串
    description可能返回 String 或null
    isDeprecated如果不应再使用此枚举值,则返回true ,否则返回 false。
    deprecationReason(可选)提供不推荐使用此枚举值的原因。

    4.2.6__指令类型#

    该__Directive类型表示服务支持的指令。
    这包括任何内置指令和任何自定义指令。
    单个指令只能在明确支持的位置使用。枚举中列出了所有可能的位置__DirectiveLocation:
    "QUERY"
    "MUTATION"
    "SUBSCRIPTION"
    "FIELD"
    "FRAGMENT_DEFINITION"
    "FRAGMENT_SPREAD"
    "INLINE_FRAGMENT"
    "VARIABLE_DEFINITION"
    "SCHEMA"
    "SCALAR"
    "OBJECT"
    "FIELD_DEFINITION"
    "ARGUMENT_DEFINITION"
    "INTERFACE"
    "UNION"
    "ENUM"
    "ENUM_VALUE"
    "INPUT_OBJECT"
    "INPUT_FIELD_DEFINITION"
    领域:
    name必须返回一个字符串
    description可能返回 String 或null
    locations``__DirectiveLocation返回一个表示该指令可能放置的有效位置的列表。
    args``__InputValue返回表示该指令接受的参数的列表。
    isRepeatable必须返回一个布尔值,指示该指令是否可以在单个位置重复使用。

    5 验证#

    GraphQL 不仅验证请求在语法上是否正确,而且还确保它在给定 GraphQL 模式的上下文中是明确且无错误的。
    无效的请求在技术上仍然是可执行的,并且将始终产生由执行部分中的算法定义的稳定结果,但是相对于包含验证错误的请求,该结果可能是不明确的、令人惊讶的或意外的,因此执行应该只发生在有效的请求。
    通常,验证是在请求执行前立即在请求的上下文中执行的,但是,如果已知之前已验证过完全相同的请求,则 GraphQL 服务可能会在不显式验证请求的情况下执行请求。例如:请求可以在开发期间验证,前提是它以后不会更改,或者服务可以验证请求一次并记住结果以避免将来再次验证相同的请求。任何客户端或开发时工具都应报告验证错误,并且不允许制定或执行已知在给定时间点无效的请求。
    类型系统的演变
    随着 GraphQL 类型系统架构随着时间的推移通过添加新类型和新字段而不断发展,以前有效的请求以后可能会变得无效。任何可能导致先前有效的请求变得无效的更改都被视为重大更改。鼓励 GraphQL 服务和模式维护者避免重大更改,但是为了对这些重大更改更有弹性,复杂的 GraphQL 系统可能仍然允许执行在某些时候已知没有任何验证错误的请求 , 并且此后一直没有改变。
    例子
    对于此模式的这一部分,我们将假设以下类型系统来演示示例:
    示例 101type Query {
      dog: Dog
    }
    
    enum DogCommand {
      SIT
      DOWN
      HEEL
    }
    
    type Dog implements Pet {
      name: String!
      nickname: String
      barkVolume: Int
      doesKnowCommand(dogCommand: DogCommand!): Boolean!
      isHouseTrained(atOtherHomes: Boolean): Boolean!
      owner: Human
    }
    
    interface Sentient {
      name: String!
    }
    
    interface Pet {
      name: String!
    }
    
    type Alien implements Sentient {
      name: String!
      homePlanet: String
    }
    
    type Human implements Sentient {
      name: String!
      pets: [Pet!]
    }
    
    enum CatCommand {
      JUMP
    }
    
    type Cat implements Pet {
      name: String!
      nickname: String
      doesKnowCommand(catCommand: CatCommand!): Boolean!
      meowVolume: Int
    }
    
    union CatOrDog = Cat | Dog
    union DogOrHuman = Dog | Human
    union HumanOrAlien = Human | Alien

    5.1 文件#

    5.1.1 可执行定义#

    正式规格#
    对于文档中的每个定义的定义。
    定义必须是 ExecutableDefinition(它不能是 TypeSystemDefinitionOrExtension)。
    说明文字#
    GraphQL 执行只会考虑可执行定义 Operation 和 Fragment。类型系统定义和扩展不可执行,并且在执行期间不会被考虑。
    为了避免歧义,包含 TypeSystemDefinitionOrExtension 的文档对于执行无效。
    不打算直接执行的 GraphQL 文档可能包含 TypeSystemDefinitionOrExtension。
    例如,以下文档对于执行无效,因为原始执行模式可能不知道提供的类型扩展:
    反例#102query getDogName {
      dog {
        name
        color
      }
    }
    
    extend type Dog {
      color: String
    }

    5.2 运营#

    5.2.1 命名操作定义#

    5.2.1.1 操作名称唯一性#

    正式规格#
    对于文档中的每个操作定义操作。
    令 operationName 为操作的名称。
    如果操作名称存在
    令操作为名为operationName的文档中的所有操作定义。
    操作必须是一组操作。
    说明文字#
    当通过其名称引用时,每个命名操作定义在文档中必须是唯一的。
    例如,以下文档是有效的:
    示例 103query getDogName {
      dog {
        name
      }
    }
    
    query getOwnerName {
      dog {
        owner {
          name
        }
      }
    }
    虽然该文件无效:
    反例第 104 号query getName {
      dog {
        name
      }
    }
    
    query getName {
      dog {
        owner {
          name
        }
      }
    }
    即使每个操作的类型不同也是无效的:
    反例#105query dogOperation {
      dog {
        name
      }
    }
    
    mutation dogOperation {
      mutateDog {
        id
      }
    }

    5.2.2匿名操作定义#

    5.2.2.1单独匿名操作#

    正式规格#
    令操作为文档中的所有操作定义。
    令anonymous为文档中的所有匿名操作定义。
    如果操作是大于 1 的集合:
    匿名必须为空。
    说明文字#
    当文档中仅存在一个操作时,GraphQL 允许使用简写形式来定义查询操作。
    例如,以下文档是有效的:
    示例 106{
      dog {
        name
      }
    }
    虽然该文件无效:
    反例第 107 号{
      dog {
        name
      }
    }
    
    query getName {
      dog {
        owner {
          name
        }
      }
    }

    5.2.3 订阅操作定义#

    5.2.3.1 单根田#

    正式规格#
    对于文档中的每个订阅操作定义订阅
    令subscriptionType为schema中的根订阅类型。
    让SelectionSet成为subscription上的顶级选择集。
    令variableValues为空集。
    令groupedFieldSet 为 CollectFields ( subscriptionType , SelectionSet , variableValues )的结果。
    groupedFieldSet必须只有一个条目,该条目不能是内省字段。
    说明文字#
    订阅操作必须只有一个根字段。
    有效示例:
    示例 108subscription sub {
      newMessage {
        body
        sender
      }
    }
    示例 109subscription sub {
      ...newMessageFields
    }
    
    fragment newMessageFields on Subscription {
      newMessage {
        body
        sender
      }
    }
    无效的:
    反例 110subscription sub {
      newMessage {
        body
        sender
      }
      disallowedSecondRootField
    }
    反例第 111 号subscription sub {
      ...multipleSubscriptions
    }
    
    fragment multipleSubscriptions on Subscription {
      newMessage {
        body
        sender
      }
      disallowedSecondRootField
    }
    订阅操作的根字段不能是内省字段。下面的例子也是无效的:
    反例 112subscription sub {
      __typename
    }
    注意:虽然每个订阅必须恰好有一个根字段,但文档可以包含任意数量的操作,每个操作都可以包含不同的根字段。执行时,包含多个订阅操作的文档必须提供 GetOperation 中描述的操作名称。

    5.3 字段#

    5.3.1 字段选择#

    字段选择必须存在于对象、接口和联合类型上。
    正式规格#
    对于文档中的每个选择。
    让fieldName为选择的目标字段``
    fieldName必须在范围内的类型上定义
    说明文字#
    字段选择的目标字段必须在选择集的范围类型上定义。别名没有限制。
    例如,以下片段将无法通过验证:
    反例 113fragment fieldNotDefined on Dog {
      meowVolume
    }
    
    fragment aliasedLyingFieldTargetNotDefined on Dog {
      barkVolume: kawVolume
    }
    对于接口来说,直接字段选择只能在字段上进行。具体实现者的字段与给定接口类型选择集的有效性无关。
    例如,以下内容是有效的:
    示例 114fragment interfaceFieldSelection on Pet {
      name
    }
    并且以下内容无效:
    反例 115fragment definedOnImplementorsButNotInterface on Pet {
      nickname
    }
    由于联合不定义字段,因此不能直接从联合类型选择集中选择字段,元字段__typename除外。联合类型选择集中的字段只能通过片段间接查询。
    例如以下内容是有效的:
    示例 116fragment inDirectFieldSelectionOnUnion on CatOrDog {
      __typename
      ... on Pet {
        name
      }
      ... on Dog {
        barkVolume
      }
    }
    但以下是无效的:
    反例 117fragment directFieldSelectionOnUnion on CatOrDog {
      name
      barkVolume
    }

    5.3.2 字段选择合并#

    正式规格#
    令set为 GraphQL 文档中定义的任何选择集。
    FieldsInSetCanMerge ( set )必须为 true。
    FieldsInSetCanMerge(设置)
    1.
    令fieldsForName为集合中具有给定响应名称的选择集,包括访问片段和内联片段。
    2.
    给定fieldsForName中的每对成员fieldA和fieldB:``
    1.
    SameResponseShape ( fieldA , fieldB )必须为 true。
    2.
    如果fieldA和fieldB的父类型相等或者其中一个不是对象类型:
    a. fieldA和fieldB必须具有相同的字段名称。
    b. fieldA和fieldB必须具有相同的参数集。
    c. 令mergedSet为将fieldA的选择集与fieldB的选择集相加的结果。
    d. FieldsInSetCanMerge ( mergedSet )必须为 true。
    SameResponseShape(字段 A,字段 B)
    1.
    令typeA为``fieldA的返回类型。
    2.
    令typeB为``fieldB的返回类型。
    3.
    如果typeA或typeB为非空。
    a. 如果typeA或typeB可为空,则返回 false。
    b. 令typeA为typeA的可为空类型。
    c. 令typeB为typeB的可为空类型。
    4.
    如果type A或type B是 List。
    a. 如果typeA或typeB不是 List,则返回 false。
    b. 令typeA为typeA的项目类型。
    c. 令typeB为typeB的项目类型。
    d. 从步骤 3 开始重复。
    5.
    如果type A或type B是标量或枚举。
    a. 如果type A和type B类型相同则返回 true,否则返回 false。
    6.
    断言:type A和type B都是复合类型。
    7.
    令mergedSet 为将fieldA的选择集与fieldB的选择集相加的结果。
    8.
    令fieldsForName为mergedSet中具有给定响应名称的选择集,包括访问片段和内联片段。
    9.
    给定 fieldsForName中的每对成员subfield A和subfield B:``
    a. 如果 SameResponseShape ( subfieldA , subfieldB )为 false,则返回 false。
    10.
    返回真。
    说明文字#
    如果在执行期间遇到具有相同响应名称的多个字段选择,则要执行的字段和参数以及结果值应该明确。因此,同一对象可能同时遇到的任何两个字段选择仅在它们相等时才有效。
    执行过程中,具有相同响应名称的字段的同时执行是通过 MergeSelectionSets 和 CollectFields 来完成的。
    对于简单的手写 GraphQL,此规则显然是一个明显的开发人员错误,但是嵌套片段可能会使手动检测变得困难。
    以下选择正确合并:
    示例 118fragment mergeIdenticalFields on Dog {
      name
      name
    }
    
    fragment mergeIdenticalAliasesAndFields on Dog {
      otherName: name
      otherName: name
    }
    以下情况无法合并:
    反例第 119 号fragment conflictingBecauseAlias on Dog {
      name: nickname
      name
    }
    如果它们具有相同的参数,则相同的参数也会被合并。值和变量都可以正确合并。
    例如以下正确合并:
    示例 120fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
      doesKnowCommand(dogCommand: SIT)
      doesKnowCommand(dogCommand: SIT)
    }
    
    fragment mergeIdenticalFieldsWithIdenticalValues on Dog {
      doesKnowCommand(dogCommand: $dogCommand)
      doesKnowCommand(dogCommand: $dogCommand)
    }
    以下内容未正确合并:
    反例第 121 号fragment conflictingArgsOnValues on Dog {
      doesKnowCommand(dogCommand: SIT)
      doesKnowCommand(dogCommand: HEEL)
    }
    
    fragment conflictingArgsValueAndVar on Dog {
      doesKnowCommand(dogCommand: SIT)
      doesKnowCommand(dogCommand: $dogCommand)
    }
    
    fragment conflictingArgsWithVars on Dog {
      doesKnowCommand(dogCommand: $varOne)
      doesKnowCommand(dogCommand: $varTwo)
    }
    
    fragment differingArgs on Dog {
      doesKnowCommand(dogCommand: SIT)
      doesKnowCommand
    }
    以下字段不会合并在一起,但是对于同一对象不会遇到这两个字段,因此它们是安全的:
    示例 122fragment safeDifferingFields on Pet {
      ... on Dog {
        volume: barkVolume
      }
      ... on Cat {
        volume: meowVolume
      }
    }
    
    fragment safeDifferingArgs on Pet {
      ... on Dog {
        doesKnowCommand(dogCommand: SIT)
      }
      ... on Cat {
        doesKnowCommand(catCommand: JUMP)
      }
    }
    然而,场响应必须是可以合并的形状。例如,标量值不得不同。在此示例中,someValue可能是 String或  Int:
    反例 123fragment conflictingDifferingResponses on Pet {
      ... on Dog {
        someValue: nickname
      }
      ... on Cat {
        someValue: meowVolume
      }
    }

    5.3.3 标量字段选择#

    正式规格#
    对于文档中的每个选择
    令SelectionType为选择的结果类型``
    如果SelectionType是标量或枚举:
    该选择的子选择集必须为空
    如果选择类型是接口、联合或对象
    该选择的子选择集不能为空
    说明文字#
    永远不允许对标量或枚举进行字段选择,因为它们是任何 GraphQL 操作的叶节点。
    以下内容有效。
    示例 124fragment scalarSelection on Dog {
      barkVolume
    }
    以下无效。
    反例 125fragment scalarSelectionsNotAllowedOnInt on Dog {
      barkVolume {
        sinceWhen
      }
    }
    相反,GraphQL 操作的叶字段选择必须是标量或枚举类型。不允许对没有子字段的对象、接口和联合进行叶选择。
    我们假设架构的查询根操作类型添加了以下内容:
    示例 126extend type Query {
      human: Human
      pet: Pet
      catOrDog: CatOrDog
    }
    以下示例均无效
    反例 127query directQueryOnObjectWithoutSubFields {
      human
    }
    
    query directQueryOnInterfaceWithoutSubFields {
      pet
    }
    
    query directQueryOnUnionWithoutSubFields {
      catOrDog
    }

    5.4 参数#

    为字段和指令提供参数。以下验证规则适用于这两种情况。

    5.4.1 参数名称#

    正式规格#
    对于文档中的每个参数
    令argumentName为argument的名称。
    令argumentDefinition为名为argumentName的父字段或定义提供的参数定义。
    argumentDefinition必须存在。
    说明文字#
    提供给字段或指令的每个参数都必须在该字段或指令的可能参数集中定义。
    例如,以下内容是有效的:
    示例 128fragment argOnRequiredArg on Dog {
      doesKnowCommand(dogCommand: SIT)
    }
    
    fragment argOnOptional on Dog {
      isHouseTrained(atOtherHomes: true) @include(if: true)
    }
    以下内容无效,因为command未在 上定义DogCommand。
    反例 129fragment invalidArgName on Dog {
      doesKnowCommand(command: CLEAN_UP_HOUSE)
    }
    这也是无效的,因为unless没有在 上定义@include。
    反例 130fragment invalidArgName on Dog {
      isHouseTrained(atOtherHomes: true) @include(unless: false)
    }
    为了探索更复杂的参数示例,让我们将以下内容添加到我们的类型系统中:
    示例 131type Arguments {
      multipleRequirements(x: Int!, y: Int!): Int!
      booleanArgField(booleanArg: Boolean): Boolean
      floatArgField(floatArg: Float): Float
      intArgField(intArg: Int): Int
      nonNullBooleanArgField(nonNullBooleanArg: Boolean!): Boolean!
      booleanListArgField(booleanListArg: [Boolean]!): [Boolean]
      optionalNonNullBooleanArgField(optionalBooleanArg: Boolean! = false): Boolean!
    }
    
    extend type Query {
      arguments: Arguments
    }
    争论中的顺序并不重要。因此以下两个例子都是有效的。
    示例 132fragment multipleArgs on Arguments {
      multipleRequirements(x: 1, y: 2)
    }
    
    fragment multipleArgsReverseOrder on Arguments {
      multipleRequirements(y: 2, x: 1)
    }

    5.4.2 参数唯一性#

    字段和指令将参数视为参数名称到值的映射。参数集中存在多个同名参数是不明确且无效的。
    正式规格#
    对于文档中的每个参数。
    令argumentName为argument的名称。
    令 arguments 为在包含参数的参数集中,所有名称为 argumentName 的参数。
    argument必须是仅包含argument 的集合。

    5.4.2.1 必需的参数#

    对于文档中的每个字段或指令。
    令参数为字段或指令提供的参数。
    令argumentDefinitions为该字段或指令的参数定义集。
    对于argumentDefinitions中的每个argumentDefinition:
    令type为argumentDefinition的预期类型。
    令defaultValue为argumentDefinition的默认值。
    如果type为 Non-Null 并且defaultValue不存在:
    令argumentName为argumentDefinition的名称。
    令 argument 为 arguments 中名称为 argumentName 的参数。
    argument必须存在。
    令value为argument的值。
    值不能是空文字。
    说明文字#
    可能需要参数。如果参数类型非空且没有默认值,则需要参数。否则,该参数是可选的。
    例如,以下内容是有效的:
    示例 133fragment goodBooleanArg on Arguments {
      booleanArgField(booleanArg: true)
    }
    
    fragment goodNonNullArg on Arguments {
      nonNullBooleanArgField(nonNullBooleanArg: true)
    }
    可以从具有可为空参数的字段中省略该参数。
    因此以下片段是有效的:
    示例 134fragment goodBooleanArgDefault on Arguments {
      booleanArgField
    }
    但这对于必需的参数无效。
    反例 135fragment missingRequiredArg on Arguments {
      nonNullBooleanArgField
    }
    提供显式值null也是无效的,因为所需参数始终具有非 null 类型。
    反例 136fragment missingRequiredArg on Arguments {
      nonNullBooleanArgField(nonNullBooleanArg: null)
    }

    5.5 片段#

    5.5.1 片段声明#

    5.5.1.1 片段名称唯一性#

    正式规格#
    对于文档中的每个片段定义片段
    令fragmentName为片段的名称。
    令fragments为名为fragmentName的文档中的所有片段定义。
    片段必须是一组。
    说明文字#
    片段定义在片段展开中按名称引用。为了避免歧义,每个片段的名称在文档中必须是唯一的。
    内联片段不被视为片段定义,并且不受此验证规则的影响。
    例如,以下文档是有效的:
    示例 137{
      dog {
        ...fragmentOne
        ...fragmentTwo
      }
    }
    
    fragment fragmentOne on Dog {
      name
    }
    
    fragment fragmentTwo on Dog {
      owner {
        name
      }
    }
    虽然该文件无效:
    反例 138{
      dog {
        ...fragmentOne
      }
    }
    
    fragment fragmentOne on Dog {
      name
    }
    
    fragment fragmentOne on Dog {
      owner {
        name
      }
    }

    5.5.1.2 片段传播类型存在#

    正式规格#
    对于文档中的每个命名跨页namedSpread
    让fragment成为namedSpread的目标``
    片段的目标类型必须在模式中定义
    说明文字#
    必须在架构中存在的类型上指定片段。这适用于命名片段和内联片段。如果它们未在架构中定义,则该片段无效。
    例如,以下片段是有效的:
    示例 139fragment correctType on Dog {
      name
    }
    
    fragment inlineFragment on Dog {
      ... on Dog {
        name
      }
    }
    
    fragment inlineFragment2 on Dog {
      ... @include(if: true) {
        name
      }
    }
    并且以下内容不验证:
    反例 140fragment notOnExistingType on NotInSchema {
      name
    }
    
    fragment inlineNotExistingType on Dog {
      ... on NotInSchema {
        name
      }
    }

    5.5.1.3 复合类型的片段#

    正式规格#
    对于文档中定义的每个片段。
    片段的目标类型必须具有 UNION、INTERFACE 或 OBJECT 类型。
    说明文字#
    片段只能在联合、接口和对象上声明。它们对标量无效。它们只能应用于非叶田。此规则适用于内联片段和命名片段。
    以下片段声明是有效的:
    示例 141fragment fragOnObject on Dog {
      name
    }
    
    fragment fragOnInterface on Pet {
      name
    }
    
    fragment fragOnUnion on CatOrDog {
      ... on Dog {
        name
      }
    }
    并且以下内容无效:
    反例 142fragment fragOnScalar on Int {
      something
    }
    
    fragment inlineFragOnScalar on Dog {
      ... on Boolean {
        somethingElse
      }
    }

    5.5.1.4 必须使用片段#

    正式规格#
    对于文档中定义的每个片段。
    片段必须是文档中至少一个跨页的目标
    说明文字#
    定义的片段必须在文档内使用。
    例如,以下是无效文档:
    反例 143fragment nameFragment on Dog { # unused
      name
    }
    
    {
      dog {
        name
      }
    }

    5.5.2 片段扩展#

    字段选择也是通过将片段分散到彼此中来确定的。将目标片段的选择集组合到引用目标片段的级别的选择集中。

    5.5.2.1 已定义片段扩展的目标#

    正式规格#
    对于文档中的每个namedSpread 。
    让fragment成为namedSpread的目标``
    片段必须在文档中定义
    说明文字#
    命名的片段跨页必须引用文档中定义的片段。如果未定义传播目标,则会出现验证错误。
    反例第 144 号{
      dog {
        ...undefinedFragment
      }
    }

    5.5.2.2 禁止片段扩展形成循环#

    正式规格#
    对于文档中的每个fragmentDefinition。
    令访问为空集。
    DetectFragmentCycles(fragmentDefinition,已访问)
    DetectFragmentCycles(fragmentDefinition,已访问)
    1.
    令spreads为fragmentDefinition的所有fragment spread 后代。
    2.
    对于spreads中的每个spread。
    1.
    访问的内容不得包含传播
    2.
    令nextVisited为包含传播和访问的成员的集合``
    3.
    让nextFragmentDefinition作为传播的目标``
    4.
    DetectFragmentCycles ( nextFragmentDefinition , nextVisited )
    说明文字#
    碎片扩散图不得形成任何循环,包括扩散本身。否则,操作可能会无限传播或在基础数据的循环中无限执行。
    这会使片段失效,从而导致无限传播:
    反例 145{
      dog {
        ...nameFragment
      }
    }
    
    fragment nameFragment on Dog {
      name
      ...barkVolumeFragment
    }
    
    fragment barkVolumeFragment on Dog {
      barkVolume
      ...nameFragment
    }
    如果将上述片段内联,这将导致无限大:
    示例 146{
      dog {
        name
        barkVolume
        name
        barkVolume
        name
        barkVolume
        name
        # forever...
      }
    }
    这也会使片段失效,在针对循环数据执行时会导致无限递归:
    反例 147{
      dog {
        ...dogFragment
      }
    }
    
    fragment dogFragment on Dog {
      name
      owner {
        ...ownerFragment
      }
    }
    
    fragment ownerFragment on Human {
      name
      pets {
        ...dogFragment
      }
    }

    5.5.2.3 片段传播是可能的#

    正式规格#
    对于文档中定义的每个跨页(命名或内联)。
    让fragment作为传播的目标。
    设fragmentType为fragment的类型条件。
    令parentType为包含spread的选择集的类型。
    令 applicableTypes 为 GetPossibleTypes(fragmentType) 和 GetPossibleTypes(parentType) 的交集。
    applyTypes不得为空。
    获取可能的类型(类型)
    1.
    如果type是对象类型,则返回包含类型的集合。
    2.
    如果type是接口类型,则返回实现类型的类型集。
    3.
    如果type是联合类型,则返回type的可能类型的集合。
    说明文字#
    片段是在类型上声明的,并且仅当运行时对象类型与类型条件匹配时才应用。它们也在父类型的上下文中传播。仅当其类型条件可以应用于父类型时,片段传播才有效。
    5.5.2.3.1 对象在对象范围内展开#
    在对象类型的范围内,唯一有效的对象类型片段扩展是适用于范围内相同类型的对象类型片段扩展。
    例如
    示例 148fragment dogFragment on Dog {
      ... on Dog {
        barkVolume
      }
    }
    并且以下内容无效
    反例 149fragment catInDogFragmentInvalid on Dog {
      ... on Cat {
        meowVolume
      }
    }
    5.5.2.3.2 抽象在对象范围内展开#
    在对象类型的范围内,如果对象类型实现接口或者是联合的成员,则可以使用联合或接口扩展。
    例如
    示例 150fragment petNameFragment on Pet {
      name
    }
    
    fragment interfaceWithinObjectFragment on Dog {
      ...petNameFragment
    }
    有效,因为 Dog 实现了 Pet。
    同样地
    示例 151fragment catOrDogNameFragment on CatOrDog {
      ... on Cat {
        meowVolume
      }
    }
    
    fragment unionWithObjectFragment on Dog {
      ...catOrDogNameFragment
    }
    有效,因为 Dog 是 CatOrDog 联盟的成员。值得注意的是,如果检查 CatOrDogNameFragment 的内容,您可能会注意到不会返回任何有效结果。但是,我们不会将其指定为无效,因为我们只考虑片段声明,而不考虑其主体。
    5.5.2.3.3 对象在抽象范围内展开#
    联合或接口扩展可以在对象类型片段的上下文中使用,但前提是对象类型是该接口或联合的可能类型之一。
    例如,以下片段是有效的:
    示例 152fragment petFragment on Pet {
      name
      ... on Dog {
        barkVolume
      }
    }
    
    fragment catOrDogFragment on CatOrDog {
      ... on Cat {
        meowVolume
      }
    }
    petFragment是有效的,因为 Dog 实现了接口 Pet。catOrDogFragment有效,因为 Cat 是 CatOrDog 联合体的成员。
    相比之下,以下片段无效:
    反例 153fragment sentientFragment on Sentient {
      ... on Dog {
        barkVolume
      }
    }
    
    fragment humanOrAlienFragment on HumanOrAlien {
      ... on Cat {
        meowVolume
      }
    }
    Dog 没有实现 Sentient 接口,因此SentientFragment永远无法返回有意义的结果。因此该片段无效。同样 Cat 不是联合 HumanOrAlien 的成员,它也永远不会返回有意义的结果,从而使其无效。
    5.5.2.3.4 抽象范围内的抽象展开#
    联合或接口片段可以在彼此内部使用。只要存在至少一种对象类型存在于范围和扩展的可能类型的交集中,扩展就被认为是有效的。
    例如
    示例 154fragment unionWithInterface on Pet {
      ...dogOrHumanFragment
    }
    
    fragment dogOrHumanFragment on DogOrHuman {
      ... on Dog {
        barkVolume
      }
    }
    被认为是有效的,因为 Dog 实现了接口 Pet 并且是 DogOrHuman 的成员。
    然而
    反例 155fragment nonIntersectingInterfaces on Pet {
      ...sentientFragment
    }
    
    fragment sentientFragment on Sentient {
      name
    }
    无效,因为不存在同时实现Pet和Sentient 的类型。
    接口在已实现的接口范围内扩展
    此外,接口类型片段始终可以扩展到它实现的接口范围中。
    在下面的示例中,...resourceFragment片段传播是有效的,因为Resource实现了Node.
    示例 156interface Node {
      id: ID!
    }
    
    interface Resource implements Node {
      id: ID!
      url: String
    }
    
    fragment interfaceWithInterface on Node {
      ...resourceFragment
    }
    
    fragment resourceFragment on Resource {
      url
    }

    5.6 价值观#

    5.6.1 正确类型的值#

    正式规格#
    对于文档中的每个输入 Value值。
    令type为找到的位置值中预期的类型。
    value必须可强制键入。
    说明文字#
    文字值必须与根据类型系统章节中定义的强制规则找到的位置中预期的类型兼容。
    位置中期望的类型包括由提供值的参数定义的类型、由提供值的输入对象字段定义的类型以及提供默认值的变量定义的类型。
    以下示例是值文字的有效使用:
    示例 157fragment goodBooleanArg on Arguments {
      booleanArgField(booleanArg: true)
    }
    
    fragment coercedIntIntoFloatArg on Arguments {
      # Note: The input coercion rules for Float allow Int literals.
      floatArgField(floatArg: 123)
    }
    
    query goodComplexDefaultValue($search: ComplexInput = { name: "Fido" }) {
      findDog(complex: $search)
    }
    不可强制的值(例如将 String 转换为 Int)是无效的。以下示例无效:
    反例 158fragment stringIntoInt on Arguments {
      intArgField(intArg: "123")
    }
    
    query badComplexValue {
      findDog(complex: { name: 123 })
    }

    5.6.2 输入对象字段名称#

    正式规格#
    对于文档中的每个输入对象字段inputField
    令inputFieldName为inputField的名称。
    令inputFieldDefinition为名为inputFieldName 的父输入对象类型提供的输入字段定义。
    inputFieldDefinition必须存在。
    说明文字#
    输入对象值中提供的每个输入字段都必须在该输入对象的预期类型的​​可能字段集中定义。
    例如,以下示例输入对象是有效的:
    示例 159{
      findDog(complex: { name: "Fido" })
    }
    虽然以下示例输入对象使用未在预期类型上定义的字段 “favoriteCookieFlavor”:
    反例 160{
      findDog(complex: { favoriteCookieFlavor: "Bacon" })
    }

    5.6.3 输入对象字段唯一性#

    正式规格#
    对于文档中的每个输入对象值inputObject 。
    对于inputObject中的每个inputField。
    让name为inputField的名称。
    令fields为``inputObject中名为name的所有输入对象字段。
    fields必须是仅包含inputField 的集合。
    说明文字#
    输入对象不得包含多个同名字段,否则会存在歧义,其中包括被忽略的语法部分。
    例如,以下文档将不会通过验证。
    反例 161{
      field(arg: { field: true, field: false })
    }

    5.6.4 输入对象必填字段#

    正式规格#
    对于文档中的每个输入对象。
    令fields为该输入对象提供的字段。
    令fieldDefinitions为该输入对象的输入字段定义集。
    对于fieldDefinitions中的每个fieldDefinition:
    令type为fieldDefinition的预期类型。
    令defaultValue为fieldDefinition的默认值。
    如果type为 Non-Null 并且defaultValue不存在:
    令fieldName为fieldDefinition的名称。
    令field为名为fieldName的字段中的输入字段。
    字段必须存在。
    令value为field的值。
    值不能是空文字。
    说明文字#
    可能需要输入对象字段。就像字段可能具有必需的参数一样,输入对象也可能具有必需的字段。如果输入字段具有非空类型且没有默认值,则该输入字段是必需的。否则,输入对象字段是可选的。

    5.7 指令#

    5.7.1 指令定义#

    正式规格#
    对于文档中的每个指令。
    令 directiveName 为指令的名称。
    令directiveDefinition为名为directiveName的指令。
    指令定义必须存在。
    说明文字#
    GraphQL 服务定义了它们支持的指令。对于指令的每次使用,该指令必须在该服务上可用。

    5.7.2 指令的有效位置#

    正式规格#
    对于文档中的每个指令。
    将 directiveName 设为指令的名称。
    令directiveDefinition为名为directiveName的指令。
    令locations为directiveDefinition 的有效位置。
    将 adjacent 设为受到指令影响的 AST 节点。
    adjacent 必须在 locations 中有对应的项来表示。
    说明文字#
    GraphQL 服务定义了它们支持哪些指令以及在哪里支持它们。对于指令的每次使用,该指令必须在服务已声明支持的位置使用。
    例如,以下文档将不会通过验证,因为@skip未提供QUERY有效位置。
    反例 162query @skip(if: $foo) {
      field
    }

    5.7.3 每个位置的指令都是唯一的#

    正式规格#
    对于文档中可以应用指令的每个位置:
    令指令为适用于``位置且不可重复的指令集。
    对于指令中的每个指令:
    令指令名称为指令的名称。
    令namedDirectives为directives中所有名为directiveName的指令的集合。``
    命名指令必须是一组。
    说明文字#
    指令用于描述其所适用的定义的一些元数据或行为变化。当使用多个同名指令时,预期的元数据或行为会变得不明确,因此每个位置仅允许使用每个指令之一。
    例如,以下文档将不会通过验证,因为@skip同一字段已使用两次:
    反例 163query ($foo: Boolean = true, $bar: Boolean = false) {
      field @skip(if: $foo) @skip(if: $bar)
    }
    然而,以下示例是有效的,因为@skip尽管在操作中且在同一命名字段上使用了两次,但每个位置仅使用了一次:
    示例 164query ($foo: Boolean = true, $bar: Boolean = false) {
      field @skip(if: $foo) {
        subfieldA
      }
      field @skip(if: $bar) {
        subfieldB
      }
    }

    5.8 变量#

    5.8.1 变量唯一性#

    正式规格#
    对于文档中的每个操作
    对于操作中定义的每个变量。
    令variableName为变量的名称。
    设变量为操作时名为variableName的`。
    变量必须是包含一个元素的集合。
    说明文字#
    如果任何操作定义了多个同名变量,则该操作是不明确且无效的。即使重复变量的类型相同也是无效的。
    反例 165query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
      dog {
        isHouseTrained(atOtherHomes: $atOtherHomes)
      }
    }
    定义同名变量对于多个操作都是有效的。如果两个操作引用同一个片段,则实际上可能是必要的:
    示例 166query A($atOtherHomes: Boolean) {
      ...HouseTrainedFragment
    }
    
    query B($atOtherHomes: Boolean) {
      ...HouseTrainedFragment
    }
    
    fragment HouseTrainedFragment on Query {
      dog {
        isHouseTrained(atOtherHomes: $atOtherHomes)
      }
    }

    5.8.2 变量是输入类型#

    正式规格#
    对于文档中的每个`操作
    对于每个操作中的变量:
    令variableType为变量的类型
    IsInputType ( variableType )必须为 true
    说明文字#
    变量只能是输入类型。对象、联合和接口不能用作输入。
    对于这些示例,请考虑添加以下类型系统:
    示例 167input ComplexInput {
      name: String
      owner: String
    }
    
    extend type Query {
      findDog(complex: ComplexInput): Dog
      booleanList(booleanListArg: [Boolean!]): Boolean
    }
    以下操作有效:
    示例 168query takesBoolean($atOtherHomes: Boolean) {
      dog {
        isHouseTrained(atOtherHomes: $atOtherHomes)
      }
    }
    
    query takesComplexInput($complexInput: ComplexInput) {
      findDog(complex: $complexInput) {
        name
      }
    }
    
    query TakesListOfBooleanBang($booleans: [Boolean!]) {
      booleanList(booleanListArg: $booleans)
    }
    以下操作无效:
    反例 169query takesCat($cat: Cat) {
      # ...
    }
    
    query takesDogBang($dog: Dog!) {
      # ...
    }
    
    query takesListOfPet($pets: [Pet]) {
      # ...
    }
    
    query takesCatOrDog($catOrDog: CatOrDog) {
      # ...
    }

    5.8.3 定义的所有变量用途#

    正式规格#
    对于文档中的每个操作
    对于范围内的每个变量使用,变量必须位于操作的变量列表中。
    令Fragments为该操作传递引用的每个片段
    对于片段中的每个片段
    对于片段范围内的每个变量使用,变量必须位于操作的变量列表中。
    说明文字#
    变量的作用域基于每个操作。这意味着在操作上下文中使用的任何变量都必须在该操作的顶层定义
    例如:
    示例 170query variableIsDefined($atOtherHomes: Boolean) {
      dog {
        isHouseTrained(atOtherHomes: $atOtherHomes)
      }
    }
    已验证。$ atOtherHomes由操作定义。
    相比之下,以下文档无效:
    反例 171query variableIsNotDefined {
      dog {
        isHouseTrained(atOtherHomes: $atOtherHomes)
      }
    }
    $ atOtherHomes 没有被该操作所定义。
    碎片使这个规则变得复杂。操作传递地包含的任何片段都可以访问该操作定义的变量。片段可以出现在多个操作中,因此变量用法必须与所有这些操作中的变量定义相对应。
    例如以下内容是有效的:
    示例 172query variableIsDefinedUsedInSingleFragment($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    因为isHouseTrainedFragment是在操作变量IsDefinedUsedInSingleFragment的上下文中使用的,并且该变量是由该操作定义的。
    另一方面,如果片段包含在未定义引用变量的操作中,则文档无效。
    反例 173query variableIsNotDefinedUsedInSingleFragment {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    这也适用于传递,因此以下内容也会失败:
    反例 174query variableIsNotDefinedUsedInNestedFragment {
      dog {
        ...outerHouseTrainedFragment
      }
    }
    
    fragment outerHouseTrainedFragment on Dog {
      ...isHouseTrainedFragment
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    必须在使用片段的所有操作中定义变量。
    示例 175query houseTrainedQueryOne($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    query houseTrainedQueryTwo($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    但是以下内容不验证:
    反例 176query houseTrainedQueryOne($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    query houseTrainedQueryTwoNotDefined {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    这是因为houseTrainedQueryTwoNotDefined没有定义变量 $ atOtherHomes ,但该变量由该操作中包含的isHouseTrainedFragment使用。

    5.8.4 使用的所有变量#

    正式规格#
    对于文档中的每个操作。
    令变量为该操作定义的变量
    变量中的每个变量必须在操作范围本身或该操作间接引用的任何片段中至少使用一次。``
    说明文字#
    操作定义的所有变量都必须在该操作或该操作传递包含的片段中使用。未使用的变量会导致验证错误。
    例如以下内容是无效的:
    反例 177query variableUnused($atOtherHomes: Boolean) {
      dog {
        isHouseTrained
      }
    }
    因为 $ atOtherHomes没有被引用。
    这些规则也适用于传递片段传播:
    示例 178query variableUsedInFragment($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    上面的内容是有效的,因为 $ atOtherHomes在isHouseTrainedFragment中使用,而 isHouseTrainedFragment 包含在variableUsedInFragment中。
    如果该片段没有对 $ atOtherHomes的引用,它将无效:
    反例 179query variableNotUsedWithinFragment($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedWithoutVariableFragment
      }
    }
    
    fragment isHouseTrainedWithoutVariableFragment on Dog {
      isHouseTrained
    }
    文档中的所有操作都必须使用其所有变量。
    因此,以下文档不验证。
    反例 180query queryWithUsedVar($atOtherHomes: Boolean) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    query queryWithExtraVar($atOtherHomes: Boolean, $extra: Int) {
      dog {
        ...isHouseTrainedFragment
      }
    }
    
    fragment isHouseTrainedFragment on Dog {
      isHouseTrained(atOtherHomes: $atOtherHomes)
    }
    该文档无效,因为queryWithExtraVar定义了无关变量。

    5.8.5 允许所有变量用法#

    正式规格#
    对于文档中的每个操作:
    令variableUsages为操作中包含的所有使用。
    对于variableUsages中的每个variableUsage:
    令variableName为variableUsage的变量名称。
    令variableDefinition为在操作中定义的名为variableName的 VariableDefinition。
    IsVariableUsageAllowed ( variableDefinition , variableUsage )必须为 true。
    IsVariableUsageAllowed(变量定义,变量使用)
    1.
    令variableType为variableDefinition的预期类型。
    2.
    令locationType为variableUsage所在的 Argument、ObjectField 或 ListValue 条目的预期类型。
    3.
    如果locationType是一个非空类型并且variableType不是一个非空类型:
    1.
    如果 variableDefinition存在默认值并且不是值 null ,则将hasNonNullVariableDefaultValue设为 true。
    2.
    如果variableUsage所在的 Argument 或 ObjectField 存在默认值,则将hasLocationDefaultValue设为 true。
    3.
    如果hasNonNullVariableDefaultValue不为 true 并且hasLocationDefaultValue不为 true,则返回 false。
    4.
    令nullableLocationType 为locationType的展开可为空类型。
    5.
    返回 AreTypesCompatible ( variableType , nullableLocationType )。
    4.
    返回 AreTypesCompatible ( variableType , locationType )。
    AreTypesCompatible(变量类型,位置类型)
    1.
    如果locationType是非空类型:
    1.
    如果variableType不是非空类型,则返回 false。
    2.
    令nullableLocationType为locationType的展开可为空类型。
    3.
    令nullableVariableType为变量类型的展开可为空类型。
    4.
    返回 AreTypesCompatible ( nullableVariableType , nullableLocationType )。
    2.
    否则,如果variableType是非空类型:
    1.
    令nullableVariableType为variableType的可为 null 类型。
    2.
    返回 AreTypesCompatible ( nullableVariableType , locationType )。
    3.
    否则,如果locationType是列表类型:
    1.
    如果variableType不是列表类型,则返回 false。
    2.
    令itemLocationType为locationType的展开项目类型。
    3.
    令itemVariableType为variableType的展开项类型。
    4.
    返回 AreTypesCompatible ( itemVariableType , itemLocationType )。
    4.
    否则,如果variableType是列表类型,则返回 false。
    5.
    如果variableType和locationType相同则返回 true ,否则返回 false。
    说明文字#
    变量的用法必须与其传递给的参数兼容。
    当变量在完全不匹配的类型上下文中使用时,或者如果变量中的可为空类型传递给非空参数类型时,就会发生验证失败。
    类型必须匹配:
    反例第 181 号query intCannotGoIntoBoolean($intArg: Int) {
      arguments {
        booleanArgField(booleanArg: $intArg)
      }
    }
    类型为 Int 的$ intArg不能用作类型为 Boolean 的 booleanArg的参数。
    列表基数也必须相同。例如,列表不能传递为奇异值。
    反例 182query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) {
      arguments {
        booleanArgField(booleanArg: $booleanListArg)
      }
    }
    还必须尊重可为空性。一般来说,可为 null 的变量不能传递给非 null 参数。
    反例 183query booleanArgQuery($booleanArg: Boolean) {
      arguments {
        nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
      }
    }
    对于列表类型,关于可空性的相同规则适用于外部类型和内部类型。不能将可为空列表传递给非空列表,也不能将可为空值列表传递给非空值列表。以下内容有效:
    示例 184query nonNullListToList($nonNullBooleanList: [Boolean]!) {
      arguments {
        booleanListArgField(booleanListArg: $nonNullBooleanList)
      }
    }
    但是,可为空列表不能传递给非空列表:
    反例 185query listToNonNullList($booleanList: [Boolean]) {
      arguments {
        nonNullBooleanListField(nonNullBooleanListArg: $booleanList)
      }
    }
    这将导致验证失败,因为 [T]无法传递给 [T]!。同样, [T]不能传递给 [T!]。
    当默认值存在时允许可选变量
    典型变量类型兼容性的一个值得注意的例外是,只要该变量或该位置提供默认值,就允许将具有可为空类型的变量定义提供给非空位置。
    在下面的示例中,$booleanArg允许在非空参数中使用可选变量optionalBooleanArg,因为字段参数是可选的,因为它在架构中提供了默认值。
    示例 186query booleanArgQueryWithDefault($booleanArg: Boolean) {
      arguments {
        optionalNonNullBooleanArgField(optionalBooleanArg: $booleanArg)
      }
    }
    在下面的示例中,$booleanArg允许在非空参数中使用可选变量nonNullBooleanArg,因为该变量在操作中提供默认值。为了与本规范的早期版本兼容,明确支持此行为。GraphQL 创作工具可能希望将此报告为警告,并建议替换Boolean为Boolean!以避免歧义。
    示例 187query booleanArgQueryWithDefault($booleanArg: Boolean = true) {
      arguments {
        nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
      }
    }
    注意:值 null 仍然可以在运行时提供给此类变量。如果提供空值,非空参数必须引发字段错误。

    6 执行#

    GraphQL 通过执行从请求生成响应。
    执行请求由几条信息组成:
    要使用的模式,通常仅由 GraphQL 服务提供。
    必须包含 GraphQL OperationDefinition并且可能包含 FragmentDefinition 的文档。
    可选:文档中要执行的操作的名称。
    可选:操作定义的任何变量的值。
    与正在执行的根类型相对应的初始值。从概念上讲,初始值代表通过 GraphQL 服务提供的数据的“宇宙”。GraphQL 服务通常对每个请求始终使用相同的初始值。
    根据这些信息,ExecuteRequest() 的结果产生响应,然后根据下面的响应部分进行格式化。

    6.1 执行请求#

    要执行请求,如果文档定义了多个操作,则执行器必须具有已解析的文档和要运行的选定操作名称,否则文档预计仅包含单个操作。请求的结果由根据下面“执行操作”部分执行该操作的结果决定。
    ExecuteRequest(schema、document、operationName、variableValues、initialValue)
    1.
    令operation为 GetOperation ( document , operationName )的结果。
    2.
    令coercedVariableValues为 CoerceVariableValues ( schema , operation , variableValues )的结果。
    3.
    如果operation是查询操作:
    a. 返回 ExecuteQuery (operation、schema、coercedVariableValues、initialValue )。
    1.
    否则,如果operation是突变操作:
    a. 返回 ExecuteMutation (operation、schema、coercedVariableValues、initialValue )。
    4.
    否则,如果operation是订阅操作:
    a. 返回 Subscribe (operation、schema、coercedVariableValues、initialValue )。
    GetOperation(文档,操作名称)
    1.
    如果operationName为null:
    a. 如果文档仅包含一项操作。(返回文档中包含的操作。)
    b. 否则会引发请求错误,需要operationName。
    2.
    否则:
    1.
    令operation为document中名为operationName的操作。
    2.
    如果未找到operation,则引发请求错误。
    3.
    返回operation。

    6.1.1 验证请求#

    正如验证部分中所解释的,只有通过所有验证规则的请求才应该被执行。如果已知验证错误,则应在响应的“错误”列表中报告这些错误,并且请求必须失败而不执行。
    通常,验证是在请求执行前立即在请求的上下文中执行的,但是,如果已知之前已验证过完全相同的请求,则 GraphQL 服务可能会执行请求而不立即验证它。GraphQL 服务应该只执行在某些时候已知没有任何验证错误并且此后没有更改的请求。
    例如:请求可以在开发期间验证,前提是它以后不会更改,或者服务可以验证请求一次并记住结果以避免将来再次验证相同的请求。

    6.1.2 变量值强制转换#

    如果操作定义了任何变量,则需要使用变量声明类型的输入强制规则来强制这些变量的值。如果在变量值的输入强制期间遇到请求错误,则操作将失败而不执行。
    CoerceVariableValues(schema、operation、variableValues)
    1.
    令coercedValues为空的无序 Map。
    2.
    令variableDefinitions为由操作定义的变量。
    3.
    对于variableDefinitions中的每个variableDefinition:
    令variableName为``variableDefinition的名称。
    令variableType为variableDefinition的预期类型。
    断言:IsInputType ( variableType )必须为true。
    令defaultValue为variableDefinition的默认值。
    如果variableValues为名称variableName提供了一个值,则让hasValue为true。
    令value为variableValues中为名称variableName提供的值。
    如果hasValue不为 true 并且defaultValue存在(包括null):(向coercedValues添加一个名为variableName且值为defaultValue 的条目。)
    否则,如果variableType是 Non-Nullable 类型,并且hasValue不为true或value为null,则引发请求错误。
    否则,如果hasValue为 true:
    1.
    如果值为null :_
    1.
    向coercedValues添加一个名为variableName且值为null 的条目。
    2.
    否则:
    1.
    如果无法根据variableType的输入强制规则强制转换值,则引发请求错误。
    2.
    令coercedValue为根据variableType的输入强制规则强制value的结果。
    3.
    向coercedValues添加一个名为variableName且值为coercedValue 的条目。
    4.
    返回coercedValues。
    注意:该算法与CoerceArgumentValues () 非常相似。

    6.2 执行操作#

    正如规范的“类型系统”部分所述,类型系统必须提供查询根操作类型。如果支持突变或订阅,它还必须分别提供变更或订阅根操作类型。

    6.2.1 查询#

    如果操作是查询,则操作的结果是使用查询根操作类型执行操作的顶级选择集的结果。
    执行查询操作时可以提供初始值。
    ExecuteQuery(query、schema、variableValues、initialValue)
    1.
    令queryType为schema中的根查询类型。
    2.
    断言:queryType是对象类型。
    3.
    令SelectionSet为query中的顶级选择集。
    4.
    令data为正常运行 ExecuteSelectionSet(selectionSet、queryType、initialValue、variableValues) 的结果(允许并行化)。**
    5.
    令错误为执行选择集时产生的任何字段错误。
    6.
    返回包含data和error 的无序映射。

    6.2.2 Mutation#

    如果操作是一个 mutation(变更操作),那么操作的结果就是在 mutation 根对象类型上执行操作的顶级选择集的结果。这个选择集应该按顺序执行。
    预期是 mutation 操作中的顶级字段对底层数据系统执行副作用。提供的变更操作的串行执行可以防止在这些副作用期间发生竞态条件。
    ExecuteMutation (mutation,schema,variableValues,initialValue)
    1.
    令mutationType为schema中的根 Mutation 类型。
    2.
    断言:mutationType是对象类型。
    3.
    令SelectionSet为mutation中的顶级选择集。
    4.
    令data为连续运行 ExecuteSelectionSet ( selectionSet , mutationType , initialValue , variableValues ) 的结果。
    5.
    令错误为执行选择集时产生的任何字段错误。
    6.
    返回包含data和error 的无序映射。

    6.2.3 订阅#

    如果操作是一个订阅,结果将是一个被称为“响应流”(Response Stream)的事件流,事件流中的每个事件都是在底层“源流”(Source Stream)上每个新事件执行操作的结果。
    执行订阅操作会在服务上创建一个持久函数,该函数将底层源流映射到返回的响应流。
    订阅(subscription、schema、variableValues、initialValue)
    1.
    令sourceStream为运行 CreateSourceEventStream ( subscription , schema , variableValues , initialValue )的结果。
    2.
    令responseStream为运行 MapSourceToResponseEvent 的结果(sourceStream、subscription、schema、variableValues)。
    3.
    返回响应流。
    注意:在大规模订阅系统中,Subscribe ()和 ExecuteSubscriptionEvent ()算法可以在单独的服务上运行以维持可预测的缩放属性。请参阅下面有关支持大规模订阅的部分。
    例如,考虑一个聊天应用程序。要订阅发布到聊天室的新消息,客户端发送如下请求:
    示例 188subscription NewMessages {
      newMessage(roomId: 123) {
        sender
        text
      }
    }
    当客户端订阅时,每当新消息发布到ID为“123”的聊天室时,“发送者”和“文本”的选择将被评估并发布给客户端,例如:
    示例 189{
      "data": {
        "newMessage": {
          "sender": "Hagrid",
          "text": "You're a wizard!"
        }
      }
    }
    “发布到聊天室的新消息”可以使用“Pub-Sub”系统,其中聊天室 ID 是“主题”,每个“发布”包含发送者和文本。
    事件流#
    事件流表示随着时间的推移可以观察到的一系列离散事件。作为示例,“发布-订阅”系统可以在“订阅主题”时生成事件流,并且对于该主题的每次“发布”,该事件流上都会发生一个事件。事件流可能会产生无限的事件序列,或者可能在任何点完成。事件流可能会完成以响应错误或仅仅因为不再发生任何事件。观察者可以在任何时候决定通过取消事件流来停止观察事件流,之后它不能再从该事件流接收任何事件。
    支持大规模订阅#
    支持订阅对于任何 GraphQL 服务来说都是一个重大变化。查询和突变操作是无状态的,允许通过克隆 GraphQL 服务实例进行扩展。相比之下,订阅是有状态的,需要在订阅的生命周期内维护 GraphQL 文档、变量和其他上下文。
    考虑当服务中的单台机器发生故障而导致状态丢失时系统的行为。通过使用单独的专用服务来管理订阅状态和客户端连接,可以提高耐用性和可用性。
    交付不可知论#
    GraphQL 订阅不需要任何特定的序列化格式或传输机制。订阅指定用于创建流、该流上每个有效负载的内容以及该流的关闭的算法。有意没有对消息确认、缓冲、重新发送请求或任何其他服务质量 (QoS) 细节进行规范。消息序列化、传输机制和服务质量细节应由实现服务选择。

    6.2.3.1 源流#

    源流表示事件序列,每个事件都会触发与该事件对应的 GraphQL 执行。与字段值解析一样,创建源流的逻辑是特定于应用程序的。
    CreateSourceEventStream (subscription,schema,variableValues,initialValue)
    1.
    令subscriptionType为schema中的根订阅类型。
    2.
    断言:subscriptionType是对象类型。
    3.
    令SelectionSet为subscription中的顶级选择集。
    4.
    令groupedFieldSet为 CollectFields ( subscriptionType , SelectionSet , variableValues )的结果。
    5.
    如果groupedFieldSet没有恰好一个条目,则引发请求错误。
    6.
    令fields为groupedFieldSet中第一个条目的值。
    7.
    令fieldName为fields中第一个条目的名称。注意:如果使用别名,该值不受影响。
    8.
    让field成为fields中的第一个条目。
    9.
    令argumentValues为 CoerceArgumentValues ( subscriptionType , field , variableValues )的结果。
    10.
    令fieldStream为运行 ResolveFieldEventStream ( subscriptionType , initialValue , fieldName , argumentValues )的结果。
    11.
    返回字段流。
    ResolveFieldEventStream(subscriptionType、rootValue、fieldName、argumentValues)
    1.
    令resolver为subscriptionType提供的内部函数,用于确定名为fieldName的订阅字段的已解析事件流。
    2.
    返回调用resolver的结果,提供rootValue和argumentValues。
    注意:此 ResolveFieldEventStream ()算法有意与 ResolveFieldValue ()类似,以便在任何操作类型上定义解析器时实现一致性。

    6.2.3.2 响应流#

    基础源流中的每个事件都会使用该事件作为根值来触发订阅选择集的执行。
    MapSourceToResponseEvent(sourceStream、subscription、schema、variableValues)
    1.
    返回一个新的事件流responseStream,它产生如下事件:
    2.
    对于sourceStream上的每个event:``
    1.
    令response为运行 ExecuteSubscriptionEvent(subscription、schema、variableValues、event)的结果。
    2.
    产生一个包含response的事件。
    3.
    当responseStream完成时:完成此事件流。
    ExecuteSubscriptionEvent(subscription、schema、variableValues、initialValue)
    1.
    令subscriptionType为``schema`中的根订阅类型。
    2.
    断言:subscriptionType是对象类型。
    3.
    令SelectionSet为subscription中的顶级选择集。
    4.
    令data为 正常运行 ExecuteSelectionSet(selectionSet、subscriptionType、initialValue、variableValues) 的结果(允许并行化)。
    5.
    令errors为执行选择集时产生的任何*字段错误。
    6.
    返回包含data和error 的无序映射。
    注意:ExecuteSubscriptionEvent ()算法有意与 ExecuteQuery () 类似,因为这是生成每个事件结果的方式。

    6.2.3.3 退订#

    当客户端不再希望接收订阅的有效负载时,取消订阅会取消响应流。这反过来也可能取消源流。这也是清理订阅使用的任何其他资源的好机会。
    取消订阅(响应流)
    1.
    取消响应流

    6.3 执行选择集#

    要执行选择集,需要知道正在评估的对象值和对象类型,以及是否必须串行执行,或者可以并行执行。
    首先,将选择集变成分组字段集;然后,分组字段集中的每个表示的字段都会生成响应映射中的条目。
    执行选择集(selectionSet、objectType、objectValue、variableValues)
    1.
    令groupedFieldSet为CollectFields ( objectType , SelectionSet , variableValues )的结果。
    2.
    将resultMap初始化为空的有序映射。
    3.
    对于每个groupedFieldSet作为responseKey和fields:
    令fieldName为fields中第一个条目的名称。注意:如果使用别名,该值不受影响。
    令fieldType为为objectType的字段 fieldName定义的返回类型。
    如果定义了fieldType :
    1.
    令responseValue为 ExecuteField ( objectType , objectValue , fieldType , fields , variableValues )。
    2.
    将responseValue设置为resultMap中responseKey的值。
    4.
    返回结果映射。
    注意:resultMap按照操作中首先出现的字段进行排序。下面的“现场收集”部分对此进行了更详细的解释。
    错误和非空字段
    如果在 ExecuteSelectionSet ()期间,具有非空fieldType 的字段引发字段错误,则该错误必须传播到整个选择集,要么在允许的情况下解析为 null,要么进一步传播到父字段。
    如果发生这种情况,任何尚未执行或尚未产生值的同级字段可能会被取消以避免不必要的工作。
    注意:有关此行为的更多信息, 请参阅处理字段错误。

    6.3.1 正常和串行执行#

    通常,执行器可以按照它选择的任何顺序(通常是并行的)执行分组字段集中的条目。由于除顶级突变字段之外的字段的解析必须始终是无副作用和幂等的,因此执行顺序不得影响结果,因此服务可以自由地按照它认为最佳的任何顺序执行字段条目。
    例如,假设以下分组字段设置为正常执行:
    示例 190{
      birthday {
        month
      }
      address {
        street
      }
    }
    有效的 GraphQL 执行器可以按照它选择的任何顺序解析这四个字段(但是当然必须在 之前和之前birthday解析)。month``address``street
    执行突变时,最顶部选择集中的选择将按顺序执行,从文本中第一个出现的字段开始。
    当串行执行分组字段集时,执行器必须按照分组字段集中提供的顺序考虑分组字段集中的每个条目。它必须先确定结果映射中每个要完成的项目的相应条目,然后才能继续处理分组字段集中的下一个项目:
    例如,假设以下选择集要串行执行:
    示例 191{
      changeBirthday(birthday: $newBirthday) {
        month
      }
      changeAddress(address: $newAddress) {
        street
      }
    }
    执行人必须依次:
    运行 ExecuteField (),changeBirthday在CompleteValue ()期间将{ month }正常执行子选择集。
    运行 ExecuteField (),changeAddress在CompleteValue ()期间将{ street }正常执行子选择集。
    作为一个说明性示例,假设我们有一个突变字段changeTheNumber,它返回一个包含一个字段 的对象theNumber。如果我们串行执行以下选择集:
    示例 192{
      first: changeTheNumber(newNumber: 1) {
        theNumber
      }
      second: changeTheNumber(newNumber: 3) {
        theNumber
      }
      third: changeTheNumber(newNumber: 2) {
        theNumber
      }
    }
    执行器将串行执行以下命令:
    解析changeTheNumber(newNumber: 1)字段
    正常执行{ theNumber }子选集first
    解析changeTheNumber(newNumber: 3)字段
    正常执行{ theNumber }子选集second
    解析changeTheNumber(newNumber: 2)字段
    正常执行{ theNumber }子选集third
    正确的执行器必须为该选择集生成以下结果:
    示例 193{
      "first": {
        "theNumber": 1
      },
      "second": {
        "theNumber": 3
      },
      "third": {
        "theNumber": 2
      }
    }

    6.3.2 字段集合#

    在执行之前,通过调用 CollectFields ()将选择集转换为分组字段集。分组字段集中的每个条目都是共享响应键的字段列表(如果已定义,则为别名,否则为字段名称)。这确保了具有相同响应键的所有字段(包括引用片段中的字段)同时执行。
    例如,收集此选择集的字段将收集该字段的两个实例a和一个字段b:
    示例 194{
      a {
        subfield1
      }
      ...ExampleFragment
    }
    
    fragment ExampleFragment on Query {
      a {
        subfield2
      }
      b
    }
    CollectFields ()生成的字段组的深度优先搜索顺序在执行过程中保持不变,确保字段以稳定且可预测的顺序出现在执行的响应中。
    CollectFields(objectType、selectionSet、variableValues、visitedFragments)
    1.
    如果未提供visitedFragments,则将其初始化为空集。
    2.
    将groupedFields初始化为空的有序列表映射。
    3.
    对于选择集中的每个selection:
    如果selection提供了指令@skip,则让skipDirective成为该指令。
    1.
    如果skipDirective的if参数为 true 或者是variableValues中值为true的变量,则继续选择selectionSet中的下一个选择。``
    如果selection提供了指令@include,则让includeDirective成为该指令。
    1.
    如果includeDirective的if参数不为 true,并且不是variableValues中值为 true 的变量,则继续选择SelectionSet中的下一个选择。``
    如果selection是一个字段:
    1.
    令responseKey为选择的响应键(如果定义则为别名,否则为字段名称)。
    2.
    令groupForResponseKey为``groupedFields中``responseKey的列表;如果不存在这样的列表,则将其创建为空列表。
    3.
    将选择附加到groupForResponseKey。
    如果选择是FragmentSpread:
    1.
    令fragmentSpreadName为选择的名称。
    2.
    如果fragmentSpreadName位于visitedFragments中,则继续选择selectionSet中的下一个选择。``
    3.
    将fragmentSpreadName添加到visitedFragments。
    4.
    令fragment为当前 Document 中的 Fragment,其名称为fragmentSpreadName。
    5.
    如果不存在这样的片段,则继续选择SelectionSet中的下一个选择。
    6.
    令fragmentType为fragment 的类型条件。
    7.
    如果 DoesFragmentTypeApply ( objectType , fragmentType )为 false,则继续选择SelectionSet中的下一个选择。``
    8.
    令fragmentSelectionSet为fragment的顶级选择集。
    9.
    令fragmentGroupedFieldSet为调用 CollectFields ( objectType , fragmentSelectionSet , variableValues , visitedFragments )的结果。
    10.
    对于fragmentGroupedFieldSet中的每个fragmentGroup:
    令responseKey为fragmentGroup中所有字段共享的响应密钥。
    令groupForResponseKey为groupedFields中responseKey的列表;如果不存在这样的列表,则将其创建为空列表。
    将fragmentGroup中的所有项目附加到groupForResponseKey。
    如果选择是 InlineFragment:
    1.
    令fragmentType为选择的类型条件。
    2.
    如果fragmentType不为 null并且DoesFragmentTypeApply ( objectType , fragmentType )为 false,则继续选择selectionSet中的下一个选择。``
    3.
    令fragmentSelectionSet为selection的顶级选择集。
    4.
    令fragmentGroupedFieldSet为调用 CollectFields ( objectType , fragmentSelectionSet , variableValues , visitedFragments )的结果。
    5.
    对于fragmentGroupedFieldSet中的每个fragmentGroup:``
    令responseKey为fragmentGroup中所有字段共享的响应密钥。
    令groupForResponseKey为groupedFields中``responseKey的列表;如果不存在这样的列表,则将其创建为空列表。
    将fragmentGroup中的所有项目附加到groupForResponseKey。
    4.
    返回groupedFields。
    是否 FragmentTypeApply(对象类型,片段类型)
    1.
    如果fragmentType是对象类型:
    如果objectType和fragmentType类型相同,则返回true,否则返回false。
    2.
    如果fragmentType是接口类型:
    如果objectType是``fragmentType的实现,则返回true,否则返回false。
    3.
    如果fragmentType是一个Union:
    如果objectType是``fragmentType的可能类型,则返回true,否则返回false。
    注意: CollectFields ()中评估@skip和指令的 步骤@include可以按任一顺序应用,因为它们是可交换应用的。

    6.4 执行字段#

    在所选对象类型上定义的分组字段集中请求的每个字段都将在响应映射中产生一个条目。字段执行首先强制任何提供的参数值,然后解析字段的值,最后通过递归执行另一个选择集或强制标量值来完成该值。
    ExecuteField(对象类型、对象值、字段类型、字段、变量值)
    1.
    让field成为fields中的第一个条目。
    2.
    令fieldName为field的字段名称。
    3.
    令argumentValues为 CoerceArgumentValues ( objectType , field , variableValues )的结果
    4.
    让resolvedValue为 ResolveFieldValue ( objectType , objectValue , fieldName , argumentValues )。
    5.
    返回 CompleteValue ( fieldType , fields , resolvedValue , variableValues )的结果。

    6.4.1 强制转换字段参数#

    字段可能包括提供给底层运行时以便正确生成值的参数。这些参数由类型系统中的字段定义为具有特定的输入类型。
    操作中的每个参数位置可能是一个文字值,或者是在运行时提供的变量。
    CoerceArgumentValues(对象类型、字段、变量值)
    1.
    令coercedValues为空的无序 Map。
    2.
    令argumentValues为field中提供的参数值。
    3.
    令fieldName为field的名称。
    4.
    令argumentDefinitions为``objectType为名为fieldName的字段定义的参数。
    5.
    对于argumentDefinitions中的每个argumentDefinition:
    令argumentName为argumentDefinition的名称。
    令argumentType为argumentDefinition的预期类型。
    令defaultValue为argumentDefinition的默认值。
    如果argumentValues为名称argumentName提供了一个值,则让hasValue为 true。
    令argumentValue为``argumentValues中为名称argumentName`提供的值。
    如果argumentValue是一个变量:
    令variableName为argumentValue的名称。
    如果variableValues为名称variableName提供了一个值,则让hasValue为 true。
    令value为variableValues中为名称variableName提供的值。
    否则,令value为argumentValue。
    如果hasValue不为 true 并且defaultValue存在(包括 null):
    向coercedValues添加一个名为argumentName且值为defaultValue 的条目。
    否则,如果argumentType是 Non-Nullable 类型,并且hasValue不为 true 或value为 null,则引发字段错误。
    否则,如果hasValue为 true:
    1.
    如果值为null :_
    将名为argumentName且值为 null 的条目添加到coercedValues中。
    2.
    否则,如果argumentValue是一个 Variable:
    将名为argumentName且值为 value 的条目添加到 coercedValues中。
    3.
    否则:
    如果无法根据argumentType的输入强制规则强制转换value,则引发字段错误。
    令coercedValue为根据argumentType的输入强制规则强制value的结果。
    将名为argumentName且值为coercedValue 的条目添加到coercedValues中。
    6.
    返回coercedValues。
    注意:变量值不会被强制,因为它们应该在执行 CoerceVariableValues () 中的操作之前被强制转换,并且有效的操作必须只允许使用适当类型的变量。

    6.4.2 值解析#

    虽然几乎所有 GraphQL 执行都可以通用描述,但最终暴露 GraphQL 接口的内部系统必须提供值。这是通过ResolveFieldValue公开的,它为实际值类型上的给定字段生成一个值。
    例如,这可能接受objectType Person、字段 "soulMate"和代表 John Lennon 的objectValue 。预计会产生代表小野洋子的价值。
    ResolveFieldValue (对象类型,对象值,字段名,参数值)
    1.
    令resolver为objectType提供的内部函数,用于确定名为fieldName的字段的解析值。
    2.
    返回调用resolver的结果,提供objectValue和argumentValues。
    注意:由于依赖读取底层数据库或网络服务来生成值,解析器 通常是异步的。这需要 GraphQL 执行器的其余部分来处理异步执行流。

    6.4.3 值完成#

    解析字段的值后,通过确保其符合预期的返回类型来完成此操作。如果返回类型是另一种 Object 类型,则字段执行过程将递归地继续。
    CompleteValue (fieldType,fields,result,variableValues)
    1.
    如果fieldType是非空类型:
    令innerType为fieldType的内部类型。
    令CompletedResult为调用 CompleteValue ( innerType , fields , result , variableValues )的结果。
    如果CompleteResult为 null,则引发字段错误。
    返回完成结果。
    2.
    如果result为 null(或类似于 null 的另一个内部值,例如 undefined),则返回 null。
    3.
    如果fieldType是 List 类型:
    如果结果不是值的集合,则引发字段错误。
    令innerType为fieldType的内部类型。
    返回一个列表,其中每个列表项都是调用CompleteValue ( innerType , fields , resultItem , variableValues )的结果,其中resultItem是result中的每个项目。
    4.
    如果fieldType是标量或枚举类型:
    返回 CoerceResult ( fieldType , result )的结果。
    5.
    如果fieldType是对象、接口或联合类型:
    如果fieldType是对象类型。
    1.
    令objectType为fieldType。
    否则,如果fieldType是 Interface 或 Union 类型。
    1.
    令objectType为ResolveAbstractType ( fieldType , result )。
    令subSelectionSet为调用 MergeSelectionSets ( fields )的结果。
    正常返回 ExecuteSelectionSet ( subSelectionSet , objectType , result , variableValues ) 的计算结果(允许并行化)。
    强制结果#
    值完成的主要目的是确保字段解析器返回的值根据 GraphQL 类型系统和服务架构有效。这种“动态类型检查”允许 GraphQL 为任何服务的内部运行时返回的类型提供一致的保证。
    有关 GraphQL 的内置标量如何强制结果值的更多详细信息,请参阅标量结果强制和序列化小节。
    强制结果(leafType,value)
    1.
    断言value不为 null。
    2.
    返回调用类型系统提供的内部方法的结果,用于确定给定值value的leafType的“结果强制” 。此内部方法必须返回该类型的有效值,而不是 null。否则抛出字段错误。``
    注意:如果字段解析器返回 null,则在调用 CoerceResult () 之前在 CompleteValue ()中对其进行处理。因此 CoerceResult ()的输入和输出都不能为 null。
    解析抽象类型#
    当完成具有抽象返回类型(即接口或联合返回类型)的字段时,首先必须将抽象类型解析为相关的对象类型。该决定是由内部系统使用任何适当的方式做出的。
    注意:在面向对象的环境(例如 Java 或 C#)中确定objectValue 的对象类型的常用方法是使用objectValue的类名。
    解决抽象类型(抽象类型,对象值)
    1.
    返回调用类型系统提供的内部方法的结果,该内部方法用于确定给定值objectValue的abstractType的对象类型。``
    合并选择集#
    当并行执行多个同名字段时,它们的选择集会在完成值时合并在一起,以便继续执行子选择集。
    说明具有相同名称和子选择的并行字段的示例操作。
    示例 195{
      me {
        firstName
      }
      me {
        lastName
      }
    }
    解析 的值后me,选择集将合并在一起,因此firstName和lastName可以解析为一个值。
    合并选择集(字段)
    1.
    令SelectionSet为空列表。
    2.
    对于fields中的每个字段:
    1.
    令fieldSelectionSet为field的选择集。
    2.
    如果fieldSelectionSet为 null 或为空,则继续处理下一个字段。
    3.
    将fieldSelectionSet中的所有选择追加到SelectionSet。
    3.
    返回选择集。

    6.4.4 处理字段错误#

    “字段错误”是在值解析或强制期间从特定字段引发的。虽然这些错误应该在响应中报告,但它们是通过生成部分响应来“处理”的。
    注意:这与执行开始之前引发的“请求错误” 不同。如果遇到请求错误,则不会开始执行,并且响应中不会返回任何数据。
    如果在解析字段时引发字段错误,则会像该字段返回 null 一样进行处理,并且必须将错误添加到响应中的“错误”列表中。
    如果解析字段的结果为 null(因为解析字段的函数返回 null 或因为引发字段错误),并且该字段属于某种Non-Null类型,则会引发字段错误。错误必须添加到响应中的“错误”列表中。
    如果该字段由于已添加到响应中的“错误”列表中的字段错误而返回 null ,则“错误”列表不得进一步受到影响。也就是说,每个字段只应将一个错误添加到错误列表中。
    由于Non-Null类型字段不能为 null,因此字段错误将传播以由父字段处理。如果父字段可能为 null ,则它将解析为 null,否则如果它是Non-Null类型,则字段错误将进一步传播到其父字段。
    如果一个List类型包装了一个Non-Null类型,并且该列表的一个元素解析为 null,则整个列表必须解析为 null。如果该List类型也包含在 a 中Non-Null,则字段错误将继续向上传播。
    如果从请求根到字段源的所有字段都返回类型错误Non-Null,则响应中的“数据”条目应为 null。

    7 响应#

    当 GraphQL 服务收到请求时,它必须返回格式正确的响应。服务的响应描述了执行请求的操作(如果成功)的结果,并描述了请求期间引发的任何错误。
    响应可能包含部分响应以及在字段上引发字段错误并被替换为null 的情况下的任何字段错误。

    7.1 响应格式#

    对 GraphQL 请求的响应必须是地图。
    如果请求引发任何错误,则响应映射必须包含带有 key 的条目errors。该条目的值在“错误”部分中描述。如果请求完成且未出现任何错误,则不得存在此条目。
    如果请求包含执行,则响应映射必须包含带有 key 的条目data。该条目的值在“数据”部分中描述。如果请求在执行前因语法错误、信息缺失或验证错误而失败,则不得存在此条目。
    响应映射还可能包含带有 key 的条目extensions。如果设置了此条目,则必须将映射作为其值。该条目保留给实现者以他们认为合适的方式扩展协议,因此对其内容没有其他限制。
    为了确保未来对协议的更改不会破坏现有服务和客户端,顶级响应映射不得包含除上述三个条目之外的任何条目。
    注意:当errors出现在响应中时,在序列化时首先出现可能会有所帮助,以便在调试期间响应中出现错误时更加清楚。

    7.1.1 数据#

    响应中的条目data将是所请求操作的执行结果。如果操作是查询,则此输出将是查询根操作类型的对象;如果操作是突变,则此输出将是突变根操作类型的对象。
    如果在执行开始之前发生错误,则该data条目不应出现在结果中。
    如果在执行过程中出现阻止有效响应的错误,则data响应中的条目应为null。

    7.1.2 错误#

    errors响应中的条目是一个非空的错误列表,其中每个错误都是一个映射。
    如果请求期间没有引发错误,则该errors条目不应出现在结果中。
    如果data响应中的条目不存在,则errors响应中的条目不能为空。它必须至少包含一个错误。它包含的错误应表明无法返回数据的原因。
    如果data响应中的条目存在(包括值 null),则errors响应中的条目可能包含执行期间引发的任何字段错误。如果在执行过程中出现字段错误,则它应该包含这些错误。
    请求错误#
    在执行开始之前会引发请求错误。这可能是由于所请求文档中的解析语法或验证错误、无法确定要执行哪个操作或变量的输入值无效而发生的。
    请求错误通常是请求客户端的错误。
    如果出现请求错误,则不会开始执行,并且data响应中的条目不得存在。该errors条目必须包含错误。
    字段错误#
    从特定字段执行期间会引发字段错误。这可能是由于值解析期间的内部错误或未能强制结果值而发生的。
    字段错误通常是 GraphQL 服务的错​​误。
    如果出现字段错误,则会尝试继续执行并生成部分结果(请参阅处理字段错误)。响应中的条目data必须存在。该errors条目应包括所有引发的字段错误。
    错误结果格式#
    每个错误都必须包含一个带有密钥的条目,message其中包含错误的字符串描述,旨在供开发人员作为理解和更正错误的指南。
    如果错误可以与请求的 GraphQL 文档中的特定点相关联,则它应该包含一个带有locations位置列表的键的条目,其中每个位置都是一个带有键line和 的映射column,两个正数从1描述开始关联的语法元素的。
    如果错误可以与 GraphQL 结果中的特定字段相关联,则它必须包含一个条目,该条目的键详细path说明了发生错误的响应字段的路径。这允许客户端识别null结果是故意的还是由运行时错误引起的。
    该字段应该是从响应的根开始并以与错误关联的字段结束的路径段列表。表示字段的路径段应该是字符串,表示列表索引的路径段应该是 0 索引的整数。如果错误发生在别名字段中,则错误的路径应使用别名,因为它代表响应中的路径,而不是请求中的路径。
    例如,如果在以下操作中获取其中一位好友的姓名失败:
    示例 196{
      hero(episode: $episode) {
        name
        heroFriends: friends {
          id
          name
        }
      }
    }
    响应可能如下所示:
    示例 197{
      "errors": [
        {
          "message": "Name for character with ID 1002 could not be fetched.",
          "locations": [{ "line": 6, "column": 7 }],
          "path": ["hero", "heroFriends", 1, "name"]
        }
      ],
      "data": {
        "hero": {
          "name": "R2-D2",
          "heroFriends": [
            {
              "id": "1000",
              "name": "Luke Skywalker"
            },
            {
              "id": "1002",
              "name": null
            },
            {
              "id": "1003",
              "name": "Leia Organa"
            }
          ]
        }
      }
    }
    如果遇到错误的字段被声明为Non-Null,null结果将冒泡到下一个可为空的字段。在这种情况下,path错误的 应该包含引发错误的结果字段的完整路径,即使该字段不存在于响应中。
    例如,如果上面的字段在架构中name声明了返回类型,则结果看起来会有所不同,但报告的错误将是相同的:Non-Null
    示例 198{
      "errors": [
        {
          "message": "Name for character with ID 1002 could not be fetched.",
          "locations": [{ "line": 6, "column": 7 }],
          "path": ["hero", "heroFriends", 1, "name"]
        }
      ],
      "data": {
        "hero": {
          "name": "R2-D2",
          "heroFriends": [
            {
              "id": "1000",
              "name": "Luke Skywalker"
            },
            null,
            {
              "id": "1003",
              "name": "Leia Organa"
            }
          ]
        }
      }
    }
    GraphQL 服务可能会通过 key 提供额外的错误条目extensions。如果设置了此条目,则必须将映射作为其值。该条目保留给实现者,以按照他们认为合适的方式向错误添加附加信息,并且对其内容没有附加限制。
    示例 199{
      "errors": [
        {
          "message": "Name for character with ID 1002 could not be fetched.",
          "locations": [{ "line": 6, "column": 7 }],
          "path": ["hero", "heroFriends", 1, "name"],
          "extensions": {
            "code": "CAN_NOT_FETCH_BY_ID",
            "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
          }
        }
      ]
    }
    GraphQL 服务不应向错误格式提供任何附加条目,因为它们可能与本规范未来版本中可能添加的附加条目发生冲突。
    注意:此规范的先前版本没有描述extensions错误格式的条目。虽然非指定条目并不构成违规,但仍不鼓励。
    反例 200{
      "errors": [
        {
          "message": "Name for character with ID 1002 could not be fetched.",
          "locations": [{ "line": 6, "column": 7 }],
          "path": ["hero", "heroFriends", 1, "name"],
          "code": "CAN_NOT_FETCH_BY_ID",
          "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
        }
      ]
    }

    7.2 序列化格式#

    GraphQL 不需要特定的序列化格式。但是,客户端应使用支持 GraphQL 响应中主要原语的序列化格式。特别是,序列化格式必须至少支持以下四个原语的表示:
    Map(映射)
    List(列表)
    String(字符串)
    Null(空值)
    序列化格式还应支持以下原语,每个原语代表一种常见的 GraphQL 标量类型,但是如果不直接支持任何原语,则可以使用字符串或更简单的原语作为替代:
    Boolean(布尔型)
    Int(整数型)
    Float(浮点型)
    Enum Value(枚举值)
    这并不是序列化格式可以编码的详尽列表。例如,表示日期、时间、URI 或具有不同精度的数字的自定义标量可以以给定序列化格式可以支持的任何相关格式来表示。

    7.2.1 JSON 序列化#

    JSON 是 GraphQL 最常见的序列化格式。尽管如上所述,GraphQL 不需要特定的序列化格式。
    当使用 JSON 作为 GraphQL 响应的序列化时,应使用以下 JSON 值来编码相关的 GraphQL 值:
    GraphQL 值JSON 值
    MapObject
    ListArray
    Nullnull
    StringString
    Booleantrue or false
    IntNumber
    FloatNumber
    Enum ValueString
    注意:为了保持一致性和易于表示,本文档中以 JSON 格式给出了响应示例。

    7.2.2 系列化 Map 排序#

    由于评估选择集的结果是有序的,因此序列化的结果映射应通过按照与选择集执行所定义的请求字段相同的顺序写入映射条目来保留此顺序。生成序列化响应,其中字段以与请求中出现的顺序相同的顺序表示,可以提高调试期间的可读性,并且如果可以预期属性的顺序,则可以更有效地解析响应。
    表示有序映射的序列化格式应保留执行部分中 CollectFields ()定义的请求字段的顺序。仅表示无序映射但顺序仍然隐含在序列化文本顺序中的序列化格式(例如 JSON)应保留请求字段的文本顺序。
    例如,如果请求是{ name, age },则以 JSON 进行响应的 GraphQL 服务应该以 响应{ "name": "Mark", "age": 30 },而不应该以 响应{ "age": 30, "name": "Mark" }。
    虽然 JSON 对象被指定为键值对的无序集合,但这些对以有序的方式表示。换句话说,虽然 JSON 字符串{ "name": "Mark", "age": 30 }和{ "age": 30, "name": "Mark" }编码相同的值,但它们也具有明显不同的属性顺序。
    注意:这并不违反 JSON 规范,因为客户端仍然可以将响应中的对象解释为无序映射并获得有效值。
    修改于 2023-09-19 08:42:57
    Built with