GraphQL

作業中の草案

はじめに

これはGraphQLの仕様書であり、クライアントサーバーアプリケーションのデータモデルの機能や要件を記述・実行するためのクエリ言語および実行エンジンです。

GraphQLに準拠した実装は、この仕様書で記述されたすべての標準要件を満たす必要があります(適合性を参照)。GraphQLの仕様書はOWFa 1.0ライセンスのもとで提供されています(著作権およびライセンスを参照)。

GraphQLは2012年に最初に作成され、オープン標準の開発は2015年に始まりました。これは2019年に設立されたGraphQL仕様プロジェクトの成果物であり、Joint Development Foundationによって支えられています。

GraphQL Foundationは2019年に設立され、GraphQLエコシステムの発展を支援する組織の中立的な中心点となっています。もし貴団体がGraphQLによる利点を受けている場合は、ぜひメンバーになることをご検討ください。

この仕様書はGitHub上で開発されています(graphql/graphql-spec)。貢献はGraphQLワーキンググループによって管理され、GraphQL技術運営委員会により運営されています。詳細はコントリビューションガイドをご覧ください。

GraphQLは進化を続けており、今後の版でも発展する可能性があります。以前の版はリリースタグに対応するパーマリンクで閲覧できます。最新の作業草案リリースはhttps://spec.graphql.org/draftでご確認ください。

1概要

GraphQLはクライアントアプリケーションを構築するために設計されたクエリ言語であり、直感的で柔軟な構文とシステムによってデータ要件とインタラクションを記述することができます。

例えば、このGraphQLリクエストでは、FacebookのGraphQL実装からIDが4のユーザーの名前を取得します。

例 № 1{
  user(id: 4) {
    name
  }
}

この結果として(JSON形式で)次のデータが得られます:

例 № 2{
  "user": {
    "name": "Mark Zuckerberg"
  }
}

GraphQLは、任意の計算が可能なプログラミング言語ではなく、本仕様書で定義された機能を持つアプリケーションサービスへのリクエストを行うための言語です。GraphQLの実装にあたって、特定のプログラミング言語やストレージシステムを義務付けていません。アプリケーションサービスは自らの機能を統一された言語、型システム、およびGraphQLの思想にマッピングします。これによりプロダクト開発に適した統一されたインターフェースと、強力なツール構築プラットフォームが提供されます。

GraphQLにはいくつかの設計原則があります:

これらの原則によって、GraphQLはクライアントアプリケーション開発のための強力かつ生産的な環境を提供します。稼働するGraphQLサービスと質の高いツールを使うアプリ開発者やデザイナーは、豊富なドキュメントを読んだり正式なトレーニングを受けなくてもすぐに生産的になれます。その体験を実現するためには、サービスやツールの構築者が必要です。

以下の正式仕様は、その構築者のための参照となります。言語とその文法、型システムおよびそれをクエリするイントロスペクションシステム、さらにそれらを動かす実行・検証エンジン及びアルゴリズムを記述しています。本仕様書の目標は、GraphQLツール、クライアントライブラリ、サービス実装のエコシステムの基盤と枠組みを提供することです――それは、組織やプラットフォームをまたいだ未だ築かれていないものです。私たちはその実現のため、コミュニティの皆様と協力することを楽しみにしています。

2言語

クライアントはGraphQLクエリ言語を使ってGraphQLサービスにリクエストを送信します。これらのリクエストソースは「ドキュメント」と呼ばれます。ドキュメントには、操作(クエリ、ミューテーション、サブスクリプション)、および共通構成単位であるフラグメント(データ要件の再利用を可能にします)を含めることができます。

GraphQLドキュメントは、終端記号がトークン(不可分の字句単位)である構文文法として定義されています。これらのトークンは、ソース文字パターンと一致する字句文法で定義されています。本ドキュメントでは、構文文法生成はコロン:で、字句文法生成は二重コロン::で区別されます。

GraphQLドキュメントのソーステキストはSourceCharacterの並びでなければなりません。文字の並びはTokenIgnored字句文法の並びで記述される必要があります。Ignoredを除いた字句トークンの並びは、単一のDocument構文文法で記述されていなければなりません。

注意 字句・構文文法や本ドキュメント全体で使用されているその他の記法の詳細については付録Aを参照してください。
字句解析と構文解析

GraphQLドキュメントのソーステキストは最初に字句トークン(Token)および無視トークン(Ignored)の並びに変換されます。ソーステキストは左から右にスキャンされ、字句文法生成で許可されるコードポイントの次の並びを次のトークンとして取得します。得られた字句トークンの並びをさらに左から右にスキャンし、Document構文文法にしたがって抽象構文木(AST)を生成します。

本ドキュメントの字句文法生成では曖昧性をなくし、単一の有効な字句解析のみを保証するために先読み制約を使用しています。字句トークンは、先読み制約で指定された文字が直後に続かない場合のみ有効です。

例えば、IntValueにはDigitという制約があり、Digitが後続することはできません。そのため、123という並びはトークン(12, 3)とは解釈できず、12の直後にDigit3が続くため、単一トークンだけを表すことになります。複数トークンとして解釈したい場合は文字の間にWhitespaceやその他のIgnoredを挟んでください。

注意 これは一般的には「最長一致(maximal munch)」動作と同様ですが、先読み制約によって追加の条件が含まれる場合があります。

2.1ソーステキスト

SourceCharacter
Any Unicode scalar value

GraphQLドキュメントはソーステキストから解釈されます。ソーステキストはSourceCharacterの並びであり、各SourceCharacterUnicodeスカラー値で、U+0000~U+D7FFまたはU+E000~U+10FFFFの任意のUnicodeコードポイントです(本仕様書では非公式に「文字」と呼ばれます)。

GraphQLドキュメントは、既存のツールや言語、シリアライズ形式との広い互換性や、テキストエディタやソース管理ツールでの表示問題を回避するため、ASCII範囲のみで記述されることが望ましい場合があります。非ASCIIのUnicodeスカラー値は、StringValueComment内に現れることがあります。

注意 JavaScriptやJavaなど、メモリ内でGraphQLドキュメントを表現するのにUTF-16を使用する実装では、サロゲートペアに遭遇する場合があります。これは一つの補助コードポイントをエンコードし、単一の有効なsource characterとなりますが、ペアになっていないサロゲートコードポイントは有効なsource characterではありません。

2.1.1空白

Whitespace
Horizontal Tab (U+0009)
Space (U+0020)

空白文字はソーステキストの可読性を高め、他のトークンを区切る役割があります。任意の箇所に任意の量の空白文字をトークンの前後に挿入可能です。トークン同士の間の空白はGraphQLドキュメントの意味には関係ありませんが、空白文字はStringCommentトークン内部にも現れます。

注意 GraphQLではUnicodeの「Zs」カテゴリ文字を空白として扱わないことで、テキストエディタやソース管理ツールでの誤認識を避けています。

2.1.2行終端

LineTerminator
New Line (U+000A)
Carriage Return (U+000D)New Line (U+000A)
Carriage Return (U+000D)New Line (U+000A)

空白と同様に、改行文字はソーステキストの可読性を高め字句トークンを区切るために使われます。任意の箇所に任意の量を他トークンの前後に挿入してもGraphQLドキュメントの意味には影響しません。

注意 構文エラーの行番号を報告する場合、直前までにあったLineTerminatorの数で行番号を割り出してください。

2.1.3コメント

GraphQLのソースドキュメントには、#から始まる1行コメントを含めることができます。

コメントにはSourceCharacterが含まれますが、LineTerminatorは除きます。したがって、コメントは#文字から始まり、LineTerminator(またはソース終端)に至るまで全てのSourceCharacterで構成されます。

コメントは空白と同様Ignoredとして扱われ、任意トークンの後やLineTerminatorの前に現れることができ、GraphQLドキュメントの意味には影響しません。

2.1.4不要なカンマ

Comma
,

空白や改行と同様に、カンマ(,)はソーステキストの可読性を高めたり字句トークンを区切ったりするために使用されますが、GraphQLドキュメント内では構文的や意味的には重要ではありません。

意味のないカンマ文字により、カンマの有無によって解釈される構文が大きく変わらないよう保証されています。他言語でよくあるユーザのミスを防ぎます。また、スタイルとしてトレーリングカンマや改行をリスト区切りに利用できるため、コードの可読性や保守性が向上します。

2.1.5字句トークン

GraphQLドキュメントは、ここで定義されるいくつかの種類の不可分な字句トークンで構成され、Unicode文字パターンにより字句文法で定義されます。字句トークン同士はIgnoredで区切ることができます。

トークンは、後でGraphQLの構文文法規則における終端記号として使用されます。

2.1.6無視トークン

Ignoredトークンは、可読性向上や字句トークン同士の区切りを提供しますが、それ以外は意味がなく構文文法生成で参照されません。

任意の量のIgnoredが各字句トークンの前後に現れてもかまいません。無視部分は全て意味がありませんが、SourceCharacterIgnoredで現れる場合、 字句Token内でも意味を持つことがあり、例えばStringValueには空白を含められます。ただしIgnored字句Token内部には現れず、たとえばFloatValueの間に空白は使えません。

バイト順マーク
UnicodeBOM
Byte Order Mark (U+FEFF)

バイト順マーク(Byte Order Mark)は、ファイルの先頭に出現可能な特別なunicodeコードポイントであり、プログラムがそのテキストストリームがUnicodeであることや、使用されているエンコーディングを判別できます。ファイルがしばしば連結されるため、バイト順マークは字句トークンの前後に現れることがありIgnoredとして扱われます。

2.1.7句読点

Punctuator
! $ & ( ) ... : = @ [ ] { | }

GraphQL ドキュメントは構造を記述するために句読点を含みます。GraphQL はデータ記述言語であり、プログラミング言語ではありません。そのため、GraphQL には数学的な式を記述するための句読点は含まれていません。

2.1.8名前

Letter
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m
n o p q r s t u v w x y z
Digit
0 1 2 3 4 5 6 7 8 9

GraphQL ドキュメントには多くの名前付き要素(操作、フィールド、引数、型、ディレクティブ、フラグメント、変数)が含まれます。全ての名前は同じ文法形式に従う必要があります。

GraphQLにおける名前は大文字・小文字を区別します。すなわち nameNameNAME は全て異なる名前です。アンダースコアも区別されるため、other_nameothername は別の名前です。

Name の後ろには NameContinue を続けてはなりません。つまり、Name トークンは常に最長の有効な並びです。ソース文字列 a1 は、a の直後に NameContinue 1 が続くため、2つのトークンとしては解釈できません。

注意 GraphQL の名前は、他システムとの相互運用性のため、ラテン ASCII サブセットの SourceCharacter に限定されています。
予約済みの名前

GraphQL 型システム内の Name は、仕様で定義された イントロスペクションシステム の一部でない限り、先頭が2つのアンダースコア "__" で始まってはなりません。

2.2説明

実行可能な Document や GraphQL 型システムの名前付き定義全てに記述的なドキュメントを含められることで、GraphQLはドキュメント機能を一級市民として備えています。これらはイントロスペクションを通じても利用可能であり、GraphQLサービスのドキュメントがその機能と一貫したまま保たれることが保証されます(型システムの説明参照)。

GraphQLの説明はMarkdown(CommonMarkで指定)として記述されます。説明文字列(多くの場合 BlockString)は、記述対象の定義の直前に現れます。

GraphQLの実行可能ドキュメント内の説明は純粋にドキュメント目的です。これらが実行・検証・レスポンスに影響することはありません。すべての説明とコメントを除去してもドキュメントの挙動や結果は変わりません。

これは説明がしっかり書かれた操作の一例です:

例 № 3"""
Request the current status of a time machine and its operator.
You can also check the status for a particular year.
**Warning:** certain years may trigger an anomaly in the space-time continuum.
"""
query GetTimeMachineStatus(
  "The unique serial number of the time machine to inspect."
  $machineId: ID!
  "The year to check the status for."
  $year: Int
) {
  timeMachine(id: $machineId) {
    ...TimeMachineDetails
    status(year: $year)
  }
}

"Details about a time machine and its operator."
fragment TimeMachineDetails on TimeMachine {
  id
  model
  lastMaintenance
  operator {
    name
    licenseLevel
  }
}

2.3ドキュメント

GraphQL ドキュメントは、GraphQL サービスまたはクライアントによって操作される完全なファイルやリクエスト文字列を記述します。ドキュメントには複数の定義が含まれており、それらは実行可能なもの、または GraphQL 型システムの表現となります。

ドキュメントは ExecutableDocument であり、少なくとも一つの OperationDefinition を含む場合のみ、GraphQL サービスで実行可能です。TypeSystemDefinitionOrExtension を含むドキュメントは実行してはなりません。これらを含むドキュメントを受け取った GraphQL 実行サービスは、説明的なエラーを返すべきです。

GraphQLサービスが新たなGraphQLスキーマの構築ではなく、GraphQLリクエストの実行のみを目的とする場合、ExecutableDocument のみを許可する選択も可能です。

OperationDefinition を含まない、または TypeSystemDefinitionOrExtension を含むドキュメントでも、解析や検証が可能です。これにより、クライアントツールが個別ファイル毎に現れる様々なGraphQL用途を表現できます。

ドキュメントに操作が1つしかない場合、その操作は無名でも構いません。その操作が変数もディレクティブも含まないクエリなら、query キーワードと操作名を省略した短縮形で記述できます。複数の操作を含む場合は、各操作に名前を付ける必要があります。複数操作を含むドキュメントを GraphQL サービスに送信する場合、実行したい操作の名前も指定する必要があります。

2.4操作

OperationType
query mutation subscription

GraphQL が扱う操作は次の 3 種類です:

  • query – 読み取り専用の取得。
  • mutation – 書き込みを行い、その後取得を行う。
  • subscription – 長時間にわたるリクエストで、時間経過に伴う一連のイベントに応じてデータを取得する。

各操作は省略可能な操作名とselection setで表現されます。

例えば、次のミューテーション操作はストーリーに「いいね」を付け、その後更新されたいいね数を取得するかもしれません:

Example № 4"""
Mark story 12345 as "liked"
and return the updated number of likes on the story
"""
mutation {
  likeStory(storyID: 12345) {
    story {
      likeCount
    }
  }
}
クエリ短縮形

ドキュメントに操作が1つだけ含まれ、その操作が変数を定義せずディレクティブも適用されていないクエリである場合、その操作は query キーワードと操作名を省略する短縮形で表現できます。

例えば、次の無名クエリ操作はクエリ短縮形で書かれています。

Example № 5{
  field
}

クエリ短縮形には説明(Descriptions)は許されません。

注意 以下の多くの例ではクエリ短縮構文を使用します。

2.5選択セット

操作は必要な情報の集合を選択し、その情報だけを正確に受け取ることで、過剰取得や不足取得を避けます。

selection set は、オブジェクト、ユニオン、またはインターフェース型に対する順序付きの選択(フィールド、フラグメントスプレッド、インラインフラグメント)の集合を定義します。

Example № 6{
  id
  firstName
  lastName
}

このクエリ操作では、idfirstName、および lastName フィールドが selection set を構成します。選択セットはフラグメント参照を含むこともできます。

2.6フィールド

selection set は主にフィールドで構成されます。フィールドは選択セット内で要求可能な個々の情報の一片を記述します。

一部のフィールドは複雑なデータや他データへの関係を表します。これらをさらに掘り下げるために、フィールド自身が選択セットを含むことができ、深くネストしたリクエストを可能にします。全ての GraphQL 操作は、スカラ値を返すフィールドまで選択を指定する必要があり、明確な形状のレスポンスを保証します。

例えば、次の操作は複雑なデータと関係をスカラ値まで選択します。

Example № 7{
  me {
    id
    firstName
    lastName
    birthday {
      month
      day
    }
    friends {
      name
    }
  }
}

操作のトップレベルの selection set にあるフィールドは、アプリケーションや現在の閲覧者にグローバルにアクセス可能な情報を表すことが多いです。典型的な例としては、現在ログインしている閲覧者への参照や、一意の識別子によって参照される特定の種類のデータへのアクセスが挙げられます。

Example № 8# `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.7引数

ArgumentsConst
(ArgumentConstlist)
ArgumentConst
Name:ValueConst

フィールドは概念的には値を返す関数であり、場合によってはその振る舞いを変える引数を受け取ります。これらの引数は GraphQL サービスの実装内で関数引数に直接マッピングされることが多いです。

この例では、特定のユーザー(id 引数で指定)と、指定サイズのプロフィール画像(size)を取得したいとします:

Example № 9{
  user(id: 4) {
    id
    name
    profilePic(size: 100)
  }
}

一つのフィールドに複数の引数を指定できることもあります:

Example № 10{
  user(id: 4) {
    id
    name
    profilePic(width: 100, height: 50)
  }
}
引数は順不同

引数は任意の構文順で与えても、意味的には同じです。

次の二つの操作は意味的に同一です:

Example № 11{
  picture(width: 200, height: 100)
}
Example № 12{
  picture(height: 100, width: 200)
}

2.8フィールドエイリアス

response name は、レスポンスオブジェクト内でフィールドの結果に対応するキーです。デフォルトでは response name はフィールド名を使用しますが、エイリアスを指定することで別の response name を定義できます。

この例では、異なるサイズのプロフィール画像を2つ取得し、レスポンスオブジェクトに重複したキーができないようにしています:

Example № 13{
  user(id: 4) {
    id
    name
    smallPic: profilePic(size: 64)
    bigPic: profilePic(size: 1024)
  }
}

これは次の結果を返します:

Example № 14{
  "user": {
    "id": 4,
    "name": "Mark Zuckerberg",
    "smallPic": "https://cdn.site.io/pic-4-64.jpg",
    "bigPic": "https://cdn.site.io/pic-4-1024.jpg"
  }
}

操作のトップレベルのフィールドにもエイリアスを付けることができます:

Example № 15{
  zuck: user(id: 4) {
    id
    name
  }
}

これは次の結果を返します:

Example № 16{
  "zuck": {
    "id": 4,
    "name": "Mark Zuckerberg"
  }
}

2.9フラグメント

フラグメントは GraphQL における主要な再利用単位です。

フラグメントを使うことで、繰り返し現れるフィールド選択を再利用でき、ドキュメント内の重複テキストを削減できます。インラインフラグメントは、インターフェースやユニオンに対して型条件に基づいてフィールドを条件付きで含めるために選択内で直接使用できます。

例えば、あるユーザーの友達や共通の友達について共通の情報を取得したい場合:

Example № 17query noFragments {
  user(id: 4) {
    friends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
    mutualFriends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
  }
}

繰り返しのフィールドはフラグメントに抽出して、親フラグメントや操作で合成できます。

Example № 18query withFragments {
  user(id: 4) {
    friends(first: 10) {
      ...friendFields
    }
    mutualFriends(first: 10) {
      ...friendFields
    }
  }
}

"Common fields for a user's friends."
fragment friendFields on User {
  id
  name
  profilePic(size: 50)
}

フラグメントはスプレッド演算子(...)を使って利用されます。フラグメントで選択された全てのフィールドが、フラグメント呼び出しと同じレベルのフィールド選択に追加されます。これは複数レベルにわたるフラグメントスプレッドを通じても行われます。

例えば:

Example № 19query 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)
}

操作 noFragmentswithFragments、および withNestedFragments はいずれも同じレスポンスオブジェクトを生成します。

2.9.1型条件

フラグメントは適用される型を指定しなければなりません。たとえば friendFieldsUser をクエリする文脈で使用できます。

フラグメントは入力値(スカラ、列挙、または入力オブジェクト)に対して指定することはできません。

フラグメントはオブジェクト型、インターフェース、ユニオンに対して指定できます。

フラグメント内の選択は、それが作用するオブジェクトの具象型がフラグメントの型と一致する場合にのみ値を返します。

例えば、Facebook のデータモデルを用いた次の操作では:

Example № 20query FragmentTyping {
  profiles(handles: ["zuck", "coca-cola"]) {
    handle
    ...userFragment
    ...pageFragment
  }
}

fragment userFragment on User {
  friends {
    count
  }
}

fragment pageFragment on Page {
  likers {
    count
  }
}

profiles ルートフィールドは、各要素が Page または User であり得るリストを返します。結果の要素が User であれば friends が存在し、likers は存在しません。逆に結果が Page であれば likers が存在し、friends は存在しません。

Example № 21{
  "profiles": [
    {
      "handle": "zuck",
      "friends": { "count": 1234 }
    },
    {
      "handle": "coca-cola",
      "likers": { "count": 90234512 }
    }
  ]
}

2.9.2インラインフラグメント

フラグメントは選択セット内にインラインで定義することもできます。これは、型条件に基づいてフィールドを条件付きで含めたり、選択セットにディレクティブを適用したりするのに便利です。

この標準的なフラグメント適用の機能は、上記の query FragmentTyping の例で示されました。同じことをインラインフラグメントで実現できます。

Example № 22query inlineFragmentTyping {
  profiles(handles: ["zuck", "coca-cola"]) {
    handle
    ... on User {
      friends {
        count
      }
    }
    ... on Page {
      likers {
        count
      }
    }
  }
}

インラインフラグメントは、フィールド群にディレクティブを適用するためにも使用できます。TypeCondition を省略した場合、インラインフラグメントは囲んでいる文脈と同じ型であるとみなされます。

Example № 23query inlineFragmentNoType($expandedInfo: Boolean) {
  user(handle: "zuck") {
    id
    name
    ... @include(if: $expandedInfo) {
      firstName
      lastName
      birthday
    }
  }
}

2.10入力値

フィールドおよびディレクティブの引数は、さまざまなリテラル原始値の入力値を受け入れます。入力値はスカラー、列挙値、リスト、または入力オブジェクトであり得ます。

定数として定義されていない場合(例えば DefaultValue のように)、入力値は変数として指定できます。リストや入力オブジェクトも(定数として定義されていない限り)変数を含むことができます。

2.10.1Int 値

IntValue は小数点や指数を持たない形で指定され、負数であってもよい(例: -123)。先頭に余分な 0 を持ってはいけません。

IntValue の後に Digit を続けてはなりません。つまり、IntValue トークンは常に最長の有効な並びです。ソース文字列 12 は、1 の直後に Digit2 が続くため、二つのトークンとして解釈できません。同様に 00 も単一トークンとも二つの 0 トークンとも解釈できないため無効です。

IntValue の後に .NameStart を続けてはなりません。もし .ExponentIndicator が続く場合、そのトークンは FloatValue としてのみ解釈されるべきです。他の NameStart 文字が続くことはありません。例えば 0x123123L のような列は有効な字句表現を持ちません。

2.10.2Float 値

Sign
+ -

FloatValue は小数点(例: 1.0)または指数(例: 1e50)、あるいはその両方(例: 6.0221413e23)を含み、負数であってもよいです。IntValue と同様に、先頭に余分な 0 を持ってはいけません。

FloatValue の後に Digit を続けてはなりません。つまり FloatValue トークンは常に最長の有効な並びです。ソース文字列 1.23 は、1.2 の直後に Digit3 が続くため、二つのトークンとして解釈できません。

FloatValue の後に . を続けてはなりません。例えば 1.23.4 は二つのトークン (1.2, 3.4) として解釈できません。

FloatValue の後に NameStart を続けてはなりません。例えば 0x1.2p3 は有効な字句表現を持ちません。

Note 数値リテラルである IntValueFloatValue はいずれも直後に文字(または他の NameStart)が続くことを制限しており、これは GraphQL が 10 進数のみをサポートしているため混乱や予期せぬ動作を避けるためです。

2.10.3Boolean 値

BooleanValue
true false

キーワード truefalse はそれぞれ二つのブール値を表します。

2.10.4文字列値

HexDigit
0 1 2 3 4 5 6 7 8 9
A B C D E F
a b c d e f
EscapedCharacter
" \ / b f n r t

StringValue は、以下に定義された静的意味論に従って全てのエスケープシーケンスを解釈することで Unicode テキスト 値(Unicode スカラー値の列)として評価されます。字句トークン間で無視される空白などの文字は、文字列値内部では意味を持ちます。

空文字列 "" の直後にもう一つの " が続いてはなりません。さもないとそれはブロック文字列の開始と解釈されてしまいます。例えばソース """""" は、三つの空の文字列ではなく、単一の空のブロック文字列としてのみ解釈できます。

エスケープシーケンス

シングルクォートの StringValue では、任意の Unicode スカラー値 をエスケープシーケンスで表現できます。GraphQL の文字列は C 風のエスケープ(例: \n)と、固定幅 4 桁の 16 進数(例: \u000A)および可変幅(例: \u{1F4A9})の 2 形式の Unicode エスケープを許容します。後者は絵文字などの補助文字の表現に便利です。

Unicode エスケープが表す 16 進数は、Unicode スカラー値 の範囲を記述していなければならず、そうでない場合は構文解析エラーとなります。例えば "\uDEAD""\u{110000}" は有効な StringValue と見なされるべきではありません。

エスケープシーケンスはシングルクォート文字列内でのみ意味を持ちます。ブロック文字列内ではそれらは単にその文字列の並び(例: """\n""" は U+005C, U+006E のテキストを表す)です。コメント内のエスケープシーケンスは特別な意味を持ちませんし、GraphQL ドキュメントの他の場所で使われるべきではありません。

一部のコードポイント(例: LineTerminator)は直接含められないため、エスケープシーケンスを使って表現しなければなりません。他のエスケープシーケンスは任意であり、非 ASCII の未エスケープ文字も文字列内で許可されます。もし ASCII のみをサポートするシステムで GraphQL を使う場合は、ASCII 以外の全ての Unicode 文字をエスケープで表現しても構いません。

互換性のために、補助文字は二つの固定幅 Unicode エスケープ(サロゲートペア)で表現しても構いません。例えば入力 "\uD83D\uDCA9""\u{1F4A9}" と同じ Unicode テキストを表す有効な StringValue です。ただしこの古い形式は可変幅エスケープの方が明確であるため、避けるべきです。

StringValue を生成する際、実装は制御不可能な印字不可文字(U+0000 ~ U+001F および U+007F ~ U+009F)をエスケープシーケンスで表現するべきです。他のエスケープは必須ではありませんが、実装は任意のコードポイント範囲をエスケープで表現しても構いません(例: ASCII 出力のみを生成する場合)。もし補助文字をエスケープするなら、可変幅 Unicode エスケープのみを用いるべきです。

ブロック文字列

ブロック文字列は三連引用符(""")で囲まれた文字の並びです。空白、改行、引用符、バックスラッシュはエスケープせずにそのまま使え、逐語的なテキストを可能にします。文字は全て有効な SourceCharacter でなければなりません。

ブロック文字列はインデント位置で使われることが多いため、BlockString の文字列値意味論は均一なインデントの除去や先頭・末尾の空行の除外を行います(BlockStringValue() を参照)。

例えば、次のようなブロック文字列を含む操作は:

Example № 24mutation {
  sendEmail(message: """
    Hello,
      World!

    Yours,
      GraphQL.
  """)
}

標準の引用文字列と同一です:

Example № 25mutation {
  sendEmail(message: "Hello,\n  World!\n\nYours,\n  GraphQL.")
}

ブロック文字列は先頭と末尾の空行を取り除くため、ある値に対して単一の標準的なブロック文字列表現は存在しません。ブロック文字列は可読性を考えると先頭と末尾に空行を持つ方が読みやすいとされています。

Example № 26"""
This starts with and ends with an empty line,
which makes it easier to read.
"""
Counter Example № 27"""This does not start with or end with any empty lines,
which makes it a little harder to read."""
Note 文字列値に非表示の ASCII 文字が必要な場合は、ブロック文字列ではなく適切なエスケープシーケンスを含む標準の引用文字列を使用する必要があります。
静的意味論

StringValueUnicode テキスト 値を記述します。これは一連の Unicode スカラー値 です。

これらの意味論は、ソーステキストに対して StringValue 文法を適用して Unicode テキスト を評価する方法を説明します。評価中に遭遇するエラーは文法の適用失敗と見なされ、解析エラーを生じさせなければなりません。

StringValue
""
  1. 空の列を返します。
StringValue
  1. 全ての StringCharacter を連結して評価し、Unicode テキスト を返します。
StringCharacter
  1. SourceCharacter の Unicode スカラー値を返します。
StringCharacter
  1. valueEscapedUnicode 内の HexDigit が表す 16 進値とします。
  2. アサート:value は、Unicode スカラ値の範囲内である(0x0000 以上 0xD7FF 以下 または 0xE000 以上 0x10FFFF 以下)。
  3. value の Unicode スカラー値を返します。
StringCharacter
  1. leadingValue を、最初の HexDigit の並びで表される16進数値とする。
  2. trailingValue を、2番目の HexDigit の並びで表される16進数値とする。
  3. leadingValue が 0xD800 以上 0xDBFF 以下(リーディングサロゲート)の場合:
    1. アサート:trailingValue は 0xDC00 以上 0xDFFF 以下(トレーリングサロゲート)である。
    2. (leadingValue - 0xD800) × 0x400 + (trailingValue - 0xDC00) + 0x10000 を返す。
  4. それ以外の場合:
    1. アサート:leadingValueUnicode スカラ値 の範囲内である。
    2. アサート:trailingValueUnicode スカラ値 の範囲内である。
    3. Unicode スカラ値 leadingValue に続けて、Unicode スカラ値 trailingValue からなる並びを返す。
Note もし両方のエスケープが Unicode スカラー値をエンコードしているなら、この意味論は各固定幅エスケープに対して先の意味論を適用したものと同一です。可変幅エスケープは単一の Unicode スカラー値のみをエンコードしなければなりません。
StringCharacter
  1. 下表に従って EscapedCharacter が表す Unicode スカラー値を返します。
エスケープ文字 スカラー値 文字名
" U+0022 ダブルクオート
\ U+005C バックスラッシュ (reverse solidus)
/ U+002F スラッシュ (solidus)
b U+0008 バックスペース
f U+000C フォームフィード
n U+000A ラインフィード (改行)
r U+000D キャリッジリターン
t U+0009 水平タブ
StringValue
  1. BlockString を評価して得られる Unicode テキスト を返します。
BlockString
  1. 全ての BlockStringCharacter を連結して得られる rawValue を Unicode テキストとします(空列になり得ます)。
  2. BlockStringValue(rawValue) の結果を返します。
BlockStringCharacter
  1. SourceCharacter の Unicode スカラー値を返します。
BlockStringCharacter
\"""
  1. 文字列列 """ を返します。
BlockStringValue(rawValue)
  1. linesrawValueLineTerminator で分割した結果とします。
  2. commonIndentnull とします。
  3. line について次を行います:
    1. もし linelines の最初の要素なら次の行へ進みます。
    2. lengthline の文字数とします。
    3. indentline の先頭に連続して現れる Whitespace 文字の数とします。
    4. もし indentlength より小さいなら:
      1. もし commonIndentnull であるか、または indentcommonIndent より小さいなら:
        1. commonIndentindent にします。
  4. もし commonIndentnull でないなら:
    1. line について:
      1. もし line が最初の要素なら次の行へ進みます。
      2. line の先頭から commonIndent 文字を削除します。
  5. 最初の行が Whitespace のみを含む限り:
    1. lines の最初の要素を削除します。
  6. 最後の行が Whitespace のみを含む限り:
    1. lines の最後の要素を削除します。
  7. formatted を空の文字列列とします。
  8. 各行 line について:
    1. もし line が最初の要素なら:
      1. formattedline を追加します。
    2. それ以外の場合:
      1. formatted にラインフィード文字 (U+000A) を追加します。
      2. formattedline を追加します。
  9. formatted を返します。

2.10.5Null 値

NullValue
null

ヌル値はキーワード null で表されます。

GraphQL には値の欠如を表す意味的に異なる 2 つの方法があります:

  • 明示的にリテラル値を与える: null
  • 値をまったく提供しない(暗黙的)。

例えば、次の二つのフィールド呼び出しは似ていますが同一ではありません:

Example № 28{
  field(arg: null)
  field
}

最初の呼び出しは引数 "arg" に明示的に null を与えていますが、二つ目は引数 "arg" を暗黙的に与えていません。これら二つの形式は異なる解釈をされ得ます。例えば、あるミューテーションでフィールドを削除する操作とフィールドを変更しない操作はそれぞれ別に扱われるかもしれません。どちらの形式も Non-Null 型を期待する入力には使用できません。

Note 変数でも同じ二つの方法が可能で、変数に null を与えるか、変数値自体を提供しないかのどちらかです。

2.10.6列挙値

EnumValue
Nametruefalsenull

列挙値は引用符なしの名前で表されます(例: MOBILE_WEB)。列挙値は「全て大文字」にすることが推奨されます。列挙値は対象の列挙型が既に特定されているコンテキストでのみ使用されるため、リテラルで列挙型名を提供する必要はありません。

2.10.7リスト値

ListValueConst
[]
[ValueConstlist]

リストは角括弧 [ ] で囲まれた順序付きの値列です。リストリテラルの値は任意の値リテラルあるいは変数であり得ます(例: [1, 2, 3])。

GraphQL ではカンマは省略可能なので、末尾のカンマは許可され、連続したカンマが欠損値を表すわけではありません。

意味論
ListValue
[]
  1. 新しい空のリスト値を返します。
ListValue
[Valuelist]
  1. inputList を新しい空のリスト値とします。
  2. Valuelist について次を行います:
    1. value を該当する Value を評価して得ます。
    2. valueinputList に追加します。
  3. inputList を返します。

2.10.8入力オブジェクト値

ObjectValueConst
{}
{ObjectFieldConstlist}
ObjectFieldConst
Name:ValueConst

入力オブジェクトのリテラル値は、波括弧 { } で囲まれたキー付き入力値の順不同リストです。オブジェクトリテラルの値は任意の入力値リテラルまたは変数になり得ます(例: { name: "Hello world", score: 1.0 })。入力オブジェクトのリテラル表現を「オブジェクトリテラル」と呼びます。

入力オブジェクトのフィールドは順不同

入力オブジェクトのフィールドは任意の構文順で提供でき、意味論的には同一です。

次の二つの操作は意味的に同一です:

Example № 29{
  nearestThing(location: { lon: 12.43, lat: -53.211 })
}
Example № 30{
  nearestThing(location: { lat: -53.211, lon: 12.43 })
}
意味論
ObjectValue
{}
  1. フィールドを持たない新しい入力オブジェクト値を返します。
ObjectValue
  1. inputObject をフィールド無しの新しい入力オブジェクト値とします。
  2. field について(リスト内):
    1. name をその field 内の Name とします。
    2. value をその field 内の Value を評価して得ます。
    3. 名前 name と値 value のフィールドを inputObject に追加します。
  3. inputObject を返します。

2.11変数

GraphQL の操作は変数でパラメータ化でき、再利用を最大化し、クライアント側で実行時に高コストな文字列組み立てを避けられます。

もし定数として定義されていない場合(例えば DefaultValue のように)、入力値には Variable を与えることができます。

変数は操作の先頭で定義されなければならず、その操作の実行全体でスコープを持ちます。これらの変数の値はリクエストの一部として GraphQL サービスに提供され、実行時に代入されます。

この例では、特定のデバイスのサイズに基づいてプロフィール画像のサイズを取得したいとします:

Example № 31query getZuckProfile(
  "The size of the profile picture to fetch."
  $devicePicSize: Int
) {
  user(id: 4) {
    id
    name
    profilePic(size: $devicePicSize)
  }
}

もし変数の値を JSON で提供するなら、profilePic のサイズとして 60 を要求できます:

Example № 32{
  "devicePicSize": 60
}
フラグメント内での変数の使用

変数はフラグメント内で使用できます。変数は特定の操作に対してグローバルスコープを持つため、フラグメント内で使用される変数は、そのフラグメントを(推移的に)利用するトップレベルの操作で宣言されていなければなりません。もしフラグメント内で参照される変数が、その変数を定義していない操作により含まれている場合、その操作は無効です(参照: All Variable Uses Defined)。

2.12型参照

GraphQL は引数や変数が期待するデータ型を記述します。入力型は他の入力型のリストであったり、任意の入力型の Non-Null 変種であり得ます。

意味論
Type
  1. nameName の文字列値とします。
  2. type をスキーマ内で name として定義されている型とします。
  3. type は存在しなければなりません。
  4. type を返します。
Type
  1. itemTypeType の評価結果とします。
  2. type を、itemType を包含型とする List 型とします。
  3. type を返します。
Type
  1. nullableTypeType の評価結果とします。
  2. type を、nullableType を包含型とする Non-Null 型とします。
  3. type を返します。

2.13ディレクティブ

DirectivesConst
DirectiveConstlist
DirectiveConst
@NameArgumentsConstopt

ディレクティブは、GraphQL ドキュメント内で実行時の代替挙動や型検証の挙動を記述する手段を提供します。

場合によっては、フィールド引数だけでは十分でない方法で GraphQL の実行挙動を変更するオプションを指定する必要があります(たとえばフィールドを条件付きで含める・スキップする等)。ディレクティブはこれを実行者に対する追加情報として記述することで実現します。

ディレクティブには名前と、任意の入力型の値を受け取る可能性がある引数の一覧があります。

ディレクティブは型、フィールド、フラグメント、操作に関する追加情報を記述するために使用できます。

将来の GraphQL のバージョンで新しい実行可能な機能が導入されると、それらはディレクティブを通じて公開される可能性があります。GraphQL サービスやツールはここで説明されているもの以外の任意の カスタムディレクティブ を提供することもできます。

ディレクティブの順序は重要

ディレクティブは特定の構文上の順序で提供でき、その順序が意味を持つ場合があります。

例えば、次の二つの型定義は異なる意味を持つかもしれません:

Example № 33type Person
  @addExternalFields(source: "profiles")
  @excludeField(name: "photo") {
  name: String
}
Example № 34type Person
  @excludeField(name: "photo")
  @addExternalFields(source: "profiles") {
  name: String
}

2.14スキーマ座標

schema coordinate は、GraphQL スキーマ内のschema elementを一意に識別する人間可読の文字列で、ツールが型やフィールド、その他のschema elementを参照するために利用することを意図しています。たとえば、スキーマ内の型やフィールドを参照するドキュメント内の参照、特定のフィールドが本番環境でどれだけ頻繁に照会されているかを追跡するためのログツールのルックアップキーなどが含まれます。

schema element は、スキーマ内で定義された名前付き型、フィールド、入力フィールド、列挙値、フィールド引数、ディレクティブ、またはディレクティブ引数(組み込み型や組み込みディレクティブを含む)を指すことができます。

Note メタフィールドはスキーマ内で定義されていないため、schema element には含まれません。拡張として、イントロスペクション型も schema element ではありません。

containing element は、ある schema element を構文的に含む、Name トークンが一つ少ないスキーマ要素を指します。具体的には次の通りです:

schema coordinate は常に一意であり、各 schema element はちょうど一つの可能な schema coordinate で参照されます。

schema coordinate は、定義されているか組み込みのいずれかの schema element を参照することができます。たとえば String@deprecated(reason:) は、組み込みのスキーマ要素を指す有効な schema coordinate の例です。

Note ユニオンのメンバーはスキーマ内の型を参照します。スキーマ内の型は TypeCoordinate によって識別されます。ユニオンメンバーを示す schema coordinate は存在せず、これにより上記で述べた schema coordinate の一意性が保たれます。
Parsing a Schema Coordinate

SchemaCoordinate は独立した文法であり独自の字句トークン集合を持つ自己完結型の文法です。それは Document に含まれるものではありません。SchemaCoordinate のソーステキストは SourceCharacter の列でなければなりません。

他の GraphQL ドキュメントと異なり、SchemaCoordinate は文字列中に Whitespace やその他の Ignored 文法を含んではなりません。これにより、各スキーマ座標は単一で曖昧さのない字句表現を持つことが保証されます。

Resolving a Schema Coordinate

ある schema element を参照するためには、schema coordinate を GraphQL の schema の文脈で解釈する必要があります。

もし該当する schema element が見つからない場合、resolve 関数は値を返しません(エラーは発生しません)。しかし、schema 内で schema coordinate の非葉ノードが見つからない場合はエラーが発生します。

Note 構文的にはメタフィールドやイントロスペクションスキーマの要素を schema coordinate で記述できる(例: Business.__typename__Type.fields(includeDeprecated:))が、それらは schema element ではないため、そのような座標を解決しても定義された振る舞いはありません。
TypeCoordinate
  1. typeNameName の値とします。
  2. もし存在すれば、schema 中の typeName という名前の型を返します。
MemberCoordinate
  1. typeName を最初の Name の値とします。
  2. typeschema 中の typeName という名前の型とします。
  3. アサート: type は存在し、Enum、Input Object、Object、または Interface 型でなければなりません。
  4. もし type が Enum 型なら:
    1. enumValueName を二番目の Name の値とします。
    2. もし存在すれば、type にある enumValueName という名前の列挙値を返します。
  5. それ以外で、もし type が Input Object 型なら:
    1. inputFieldName を二番目の Name の値とします。
    2. もし存在すれば、type にある inputFieldName という名前の入力フィールドを返します。
  6. それ以外の場合:
    1. fieldName を二番目の Name の値とします。
    2. もし存在すれば、typefieldName という名前のフィールドを返します。
ArgumentCoordinate
  1. typeName を最初の Name の値とします。
  2. typeschema 中の typeName という名前の型とします。
  3. アサート: type は存在し、Object か Interface 型でなければなりません。
  4. fieldName を二番目の Name の値とします。
  5. fieldtype の中の fieldName という名前のフィールドとします。
  6. アサート: field は存在しなければなりません。
  7. fieldArgumentName を三番目の Name の値とします。
  8. もし存在すれば、field の中の fieldArgumentName という名前の引数を返します。
DirectiveCoordinate
  1. directiveNameName の値とします。
  2. もし存在すれば、schema 中の directiveName という名前のディレクティブを返します。
DirectiveArgumentCoordinate
  1. directiveName を最初の Name の値とします。
  2. directiveschema 中の directiveName という名前のディレクティブとします。
  3. アサート: directive は存在しなければなりません。
  4. directiveArgumentName を二番目の Name の値とします。
  5. もし存在すれば、directive の中の directiveArgumentName という名前の引数を返します。
Examples
Element Kind Schema Coordinate Schema Element
Named Type Business Business type
Field Business.name name field on the Business type
Input Field SearchCriteria.filter filter input field on the SearchCriteria input object type
Enum Value SearchFilter.OPEN_NOW OPEN_NOW value of the SearchFilter enum
Field Argument Query.searchBusiness(criteria:) criteria argument on the searchBusiness field on the Query type
Directive @private @private directive
Directive Argument @private(scope:) scope argument on the @private directive

上の表は、以下のスキーマに基づいて各種の schema coordinate の例を示しています。

type Query {
  searchBusiness(criteria: SearchCriteria!): [Business]
}

input SearchCriteria {
  name: String
  filter: SearchFilter
}

enum SearchFilter {
  OPEN_NOW
  DELIVERS_TAKEOUT
  VEGETARIAN_MENU
}

type Business {
  id: ID
  name: String
  email: String @private(scope: "loggedIn")
}

directive @private(scope: String!) on FIELD_DEFINITION

3型システム

GraphQL の型システムは GraphQL サービスの機能を記述し、要求された操作が有効かどうかの判定、レスポンス結果の型の保証、リクエスト時に提供された変数の値が有効かどうかを判断するために使われます。

GraphQL 言語には、GraphQL サービスの型システムを記述するための IDL(インターフェース記述言語)が含まれています。ツールはこの定義言語を利用してクライアントコード生成やサービスのブートストラップなどのユーティリティを提供できます。

GraphQL のリクエストを実行することのみを目的とし、新しい GraphQL スキーマを構築しないツールやサービスは、TypeSystemDefinition を許可しない選択をすることができます。スキーマを生成することのみを目的としリクエストの実行を行わないツールは、TypeSystemDocument] のみを許可し、ExecutableDefinitionTypeSystemExtension を許可しない選択をすることができますが、存在する場合は説明的なエラーを返すべきです。

Note 型システム定義言語は、本仕様書の残りの部分で型システムの例を示す際に広く使用されます。

3.1型システム拡張

型システム拡張は、以前の型システムから拡張された GraphQL 型システムを表現するために使われます。たとえば、あるローカルサービスがクライアントがローカルでのみアクセスするデータを表現するために使う場合や、ある GraphQL サービス自体が別のサービスの拡張である場合などに利用されます。

スキーマの生成や拡張のみを目的としリクエストの実行を行わないツールは、TypeSystemExtensionDocument のみを許可し、ExecutableDefinition を許可しない選択をすることができますが、存在する場合は説明的なエラーを返すべきです。

3.2型システムの説明

ドキュメントは GraphQL 型システムの一級の機能であり、TypeSystemDocument 内の定義とともに記述され、イントロスペクションを通じて利用可能にされます。

Descriptions により、GraphQL サービス設計者はサービスの機能と一貫したドキュメントを簡単に提供できます。説明はすべての定義に対して Markdown(CommonMark に従う)で提供するべきです。

GraphQL スキーマやその他の定義(型、フィールド、引数など)で説明可能なものは、自己記述的ではない限り Description を提供するべきです。

例として、この簡単な GraphQL スキーマはよく記述されています:

Example № 35"""
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スキーマ

GraphQL サービスの集合的な型システムの機能はそのサービスの「スキーマ」と呼ばれます。スキーマはサポートする型とディレクティブ、およびクエリ・ミューテーション・サブスクリプションの各操作種別に対する root operation type によって定義されます。これにより各操作が型システムのどこから開始するかが決まります。

GraphQL スキーマ自体は内部的に有効でなければなりません。本節では関連する検証プロセスのルールを説明します。

GraphQL スキーマ内のすべての型は一意の名前を持たねばなりません。2 つの提供された型が同じ名前を持つことは許されません。提供された型は組み込み型(スカラーやイントロスペクション型を含む)と名前が衝突してはなりません。

GraphQL スキーマ内のすべてのディレクティブは一意の名前を持たねばなりません。

スキーマ内で定義されるすべての型およびディレクティブは、GraphQL のイントロスペクションシステムで排他的に使用されるため、名前が "__"(アンダースコア二つ)で始まってはなりません。

3.3.1ルート操作型

スキーマはサポートする各操作種別(query、mutation、subscription)に対して初期の root operation type を定義します。これにより各操作が型システム内でどこから始まるかが決まります。

queryroot operation type は必須であり、Object 型でなければなりません。

mutationroot operation type は任意です。提供されていない場合、サービスはミューテーションをサポートしません。提供されている場合は Object 型でなければなりません。

同様に、subscriptionroot operation type も任意です。提供されていない場合、サービスはサブスクリプションをサポートしません。提供されている場合は Object 型でなければなりません。

querymutation、および subscription のルート型は、提供されている場合は互いに異なる型でなければなりません。

query ルート型のフィールドは、GraphQL クエリ操作のトップレベルで利用可能なフィールドを示します。

例えば次の操作は:

Example № 36query {
  myName
}

は、queryroot operation type に "myName" というフィールドが存在する場合にのみ有効です:

Example № 37type Query {
  myName: String
}

同様に次のミューテーションは、mutationroot operation type に "setName" というフィールドが存在する場合にのみ有効です。

Example № 38mutation {
  setName(name: "Zuck") {
    newName
  }
}

型システム定義言語を使う際、ドキュメントには最大で一つの schema 定義しか含めてはなりません。

次の例では、GraphQL スキーマが query と mutation の両方の root operation type を持つように定義されています:

Example № 39schema {
  query: MyQueryRootType
  mutation: MyMutationRootType
}

type MyQueryRootType {
  someField: String
}

type MyMutationRootType {
  setSomeField(to: String): String
}
Default Root Operation Type Names

querymutationsubscriptionデフォルトルート型名 はそれぞれ "Query""Mutation"、および "Subscription" です。

型システム定義言語は、各ルート操作型がそれぞれのデフォルト名を使用し、他の型がデフォルト名を使用しておらず、またスキーマに説明がない場合にスキーマ定義を省略できます。

同様に、型システム定義言語で GraphQL スキーマを表現する際、各ルート操作型がそれぞれのデフォルト名を使用し、他の型がデフォルト名を使用しておらず、スキーマに説明がない場合はスキーマ定義を省略するべきです。

次の例は、"Query" 型がスキーマの query ルート型とみなされるため、明示的に schema 定義を含まなくとも有効な完全な GraphQL スキーマを表します。

Example № 40type Query {
  someField: String
}

次の例は、スキーマ定義を含めることで mutation ルート型が存在することを明確に示しており、たとえ "Mutation" という名の型があっても誤ってルート型と見なされることはありません。

Example № 41schema {
  query: Query
}

type Query {
  latestVirus: Virus
}

type Virus {
  name: String
  mutations: [Mutation]
}

type Mutation {
  name: String
}

次の例は、説明とともに query と mutation の両方の操作型を持つ有効な GraphQL スキーマを示します:

Example № 42"""
Example schema
"""
schema {
  query: Query
  mutation: Mutation
}

type Query {
  someField: String
}

type Mutation {
  someMutation: String
}

3.3.2スキーマ拡張

SchemaExtension
extendschemaDirectivesConstopt{RootOperationTypeDefinitionlist}
extendschemaDirectivesConst{

スキーマ拡張は、以前のスキーマから拡張されたスキーマを表現するために使われます。例えば既存スキーマに追加の操作型やディレクティブを追加する GraphQL サービスで利用されることがあります。

Note 追加の操作型定義を持たないスキーマ拡張は、パースの曖昧さを避けるために {(クエリ短縮など)を続けてはなりません。この制限は以下の型定義や拡張にも適用されます。
Schema Validation

スキーマ拡張は、誤って定義されると無効となる可能性があります。

  1. スキーマは既に定義されていなければなりません。
  2. 提供された非繰り返しディレクティブは、以前のスキーマに既に適用されていてはなりません。

3.4

任意の GraphQL スキーマの基本単位は型です。GraphQL には名前付き型定義が 6 種類と、2 種類のラップ型(wrapping types)があります。

最も基本的な型は Scalar です。スカラーは文字列や整数のようなプリミティブ値を表します。しばしばスカラーの可能な応答は列挙可能であり、その場合は有効な応答の集合を指定する Enum 型が提供されます。

スカラーと列挙はレスポンスツリーの葉を形成し、中間層はフィールドの集合を定義する Object 型であり、各フィールドはシステム内の別の型となり任意の型階層を定義できます。

GraphQL は 2 種類の抽象型をサポートします:インターフェースとユニオンです。

Interface はフィールドのリストを定義します。Object 型やそのインターフェースを実装する他の Interface 型はこれらのフィールドを実装することが保証されます。フィールドが Interface 型を返すと宣言されている場合、実行時には有効な実装 Object 型が返されます。

Union は可能な型のリストを定義します。インターフェースと同様に、ユニオンが返されると宣言されている場合は、その可能な型のいずれかが返されます。

最後に、フィールド引数や変数への複雑な構造体の入力が必要な場合がよくあります。Input Object 型はスキーマが期待するデータを正確に定義することを可能にします。

3.4.1ラップ型

これまでの型はすべて nullable かつ単数であると仮定されています。例えばスカラー文字列は null または単一の文字列を返します。

フィールドが別の型のリストを表すとスキーマで記述できる場合があります。そのために別の型をラップする List 型が用意されています。

同様に、Non-Null 型は別の型をラップし、その結果の値が決して null にならないことを示します(そのため実行エラーが発生しても null にはなりません)。

これら 2 つの型は「ラップ型」と呼ばれ、ラップしない型は「名前付き型」と呼ばれます。ラップ型は、型を繰り返しアンラップして名前付き型が見つかるまでの基となる名前付き型を持ちます。

3.4.2入力・出力型

型は、引数や変数への入力として受け入れられる値と、フィールドが出力する値の両方を記述するために GraphQL 全体で使用されます。これら二つの用途により型は 入力型出力型 に分類されます。Scalar や Enum のような型は入力型と出力型の両方として使えますが、他の種類の型は一方でしか使えないことがあります。Input Object 型は入力型にのみ使用できます。Object、Interface、Union 型は出力型にのみ使用できます。List と Non-Null 型は、ラップされている型がどのように使用できるかに応じて入力型または出力型として使用できます。

IsInputType(type)
  1. もし type が List 型か Non-Null 型であれば:
    1. unwrappedTypetype のアンラップされた型とします。
    2. IsInputType(unwrappedType) を返します。
  2. もし type が Scalar、Enum、または Input Object 型であれば:
    1. true を返します。
  3. false を返します。
IsOutputType(type)
  1. もし type が List 型か Non-Null 型であれば:
    1. unwrappedTypetype のアンラップされた型とします。
    2. IsOutputType(unwrappedType) を返します。
  2. もし type が Scalar、Object、Interface、Union、または Enum 型であれば:
    1. true を返します。
  3. false を返します。

3.4.3型拡張

型拡張は、以前の型から拡張された GraphQL 型を表現するために使われます。例えば、あるローカルサービスがクライアントがローカルでのみアクセスする追加フィールドを表現するために使用することがあります。

3.5スカラー

スカラー型は GraphQL 型システムにおける原始的な葉の値を表します。GraphQL のレスポンスは階層的なツリーの形を取り、このツリーの葉は通常 GraphQL のスカラー型(あるいは Enum 型や null 値)になります。

GraphQL は下記に示すいくつかの組み込みスカラーを提供しますが、型システムは追加のカスタムスカラーを導入して独自の意味を付与することもできます。

Built-in Scalars

GraphQL は明確に定義された基本的なスカラー型の集合を規定しています: IntFloatStringBoolean、および ID です。GraphQL フレームワークはこれらすべてをサポートすべきであり、同名の型を提供するサービスは本書で記述される振る舞いに従わなければなりません。たとえば、サービスが Int という型を定義して 64 ビット数値や国際化情報など別の目的に用いることは許されません。

__Schema のイントロスペクション型から型の集合を返す際、参照されている組み込みスカラーはすべて含める必要があります。スキーマ内で組み込みスカラー型がどこにも参照されていない場合(その型を持つフィールド、引数、入力フィールドがない場合)は、含めてはなりません。

型システム定義言語で GraphQL スキーマを表現する場合、簡潔さのためにすべての組み込みスカラーは省略されるべきです。

Custom Scalars

GraphQL サービスは組み込みスカラーに加えてカスタムスカラー型を使用できます。たとえば、サービスが UUID というスカラーを定義し、それが文字列としてシリアライズされるが RFC 4122 に準拠するようにすることが考えられます。UUID 型のフィールドをクエリする場合、結果を RFC 4122 準拠のパーサで解析できることを期待できます。別の有用なカスタムスカラー例として URL があり、文字列としてシリアライズされるがサービスが有効な URL であることを保証するものです。

カスタムスカラーを定義する際、GraphQL サービスは @specifiedBy ディレクティブやイントロスペクションフィールド specifiedByURL を通じて scalar specification URL を提供するべきです。この URL はそのスカラーのデータ形式、シリアライズ、強制変換ルールに関する人間可読の仕様へリンクしていなければなりません。

たとえば、UUID スカラーを提供するサービスは RFC 4122 へのリンク、あるいはその RFC の合理的部分集合を定義する独自の文書をリンクすることができます。もし scalar specification URL が存在するなら、それを認識するシステムやツールはその記載ルールに従うべきです。

Example № 43scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
scalar DateTime
  @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time")

カスタムの scalar specification URL は単一で安定した形式を示すべきで、曖昧さを避けなければなりません。リンク先の仕様が流動的な場合は、変化し得るリソースではなく固定されたバージョンへリンクするべきです。

Note 一部のコミュニティ運用のカスタムスカラー仕様は scalars.graphql.org にホストされています。

カスタムの scalar specification URL は定義後に変更すべきではありません。変更するとツールチェインを混乱させたり、リンク先仕様の内容に破壊的変更をもたらす可能性があります。

組み込みスカラー型は本書で規定されているため、scalar specification URL を提供してはなりません。

Note カスタムスカラーは説明で指定フォーマットの要約や例を示すべきです。詳細は GraphQL スカラーの implementation guide を参照してください。
Result Coercion and Serialization

GraphQL サービスは、あるスカラー型のフィールドを準備する際に、そのスカラー型が示す契約を守らなければなりません。値を強制変換するか、値が強制変換できない、または変換によってデータ損失が生じる可能性がある場合は execution error を発生させるべきです。

GraphQL サービスは内部表現の異なる型を期待される戻り型へ合理的に強制変換する選択をすることがあります。たとえば Int 型のフィールドを返す際に boolean の true1 に変換したり、文字列 "123" を 10 進の 123 に解析することがあります。しかし、情報を失うような変換が合理的に行えない場合は execution error を発生させる必要があります。

この強制変換の振る舞いはクライアントからは観測できないため、正確なルールは実装に委ねられます。唯一の要件は、サービスが期待されるスカラー型に適合する値を返さなければならないという点です。

GraphQL のスカラーは使用されるシリアライズ形式に従ってシリアライズされます。各スカラー型に対して最も適切なシリアライズ原始型がある場合があり、サービスは適宜その原始型を生成すべきです。

スカラーのシリアライズに関する詳しい情報は Serialization Format を参照してください。

Input Coercion

もし GraphQL サービスが引数の入力としてスカラー型を期待する場合、強制変換は可観測でありその規則は明確に定義されていなければなりません。入力値が強制変換ルールに一致しない場合は、実行が始まる前に検証されるため request error を発生させなければなりません。

GraphQL には整数および浮動小数点の入力値を表す異なる定数リテラルがあり、どの種類の入力値が与えられるかによって強制変換ルールは異なる扱いをする可能性があります。GraphQL はしばしば変数によりパラメータ化され、その値は HTTP のようなトランスポートでシリアライズされて送信されることがあります。一般的なシリアライズ(例: JSON)は整数と浮動小数点を区別しないため、分数部が空であれば整数入力値(例: 1.0 が整数として解釈される)として扱い、そうでなければ浮動小数点入力値として扱います。

以下のすべての型について、Non-Null を除き、明示的な値 null が与えられた場合は入力強制変換の結果は null になります。

3.5.1Int

Int スカラー型は符号付き 32 ビットの非小数値を表します。32 ビット整数や数値型をサポートするレスポンス形式は、このスカラーを表現するためにその型を利用するべきです。

Result Coercion

Int 型を返すフィールドは 32 ビット整数の内部値に遭遇することを想定します。

GraphQL サービスは、情報を失うことなく合理的に非整数の内部値を整数へ強制変換できる場合はそれを行えます。そうでない場合は execution error を発生させる必要があります。例えば浮動小数点 1.01 として返す、または文字列 "123"123 に返すなどです。情報が失われる可能性がある場合は実行エラーを発生させる方が適切です。たとえば浮動小数点 1.2 を切り捨てて 1 にするのではなく実行エラーを発生させるべきです。

内部の整数値が -231 未満、または 231 以上を表す場合は、execution error が発生すべきです。

Input Coercion

入力型として期待される場合、整数の入力値のみが受け入れられます。数値を含む文字列を含むその他の入力値はすべて型不正を示す request error を発生させるべきです。整数入力値が -231 未満、または 231 以上を表す場合は request error を発生させるべきです。

Note 32 ビットより大きい数値には String やカスタム定義のスカラー型を用いるべきです。すべてのプラットフォームやトランスポートが 32 ビットより大きい整数のエンコードをサポートするとは限りません。

3.5.2Float

Float スカラー型は IEEE 754 で規定された倍精度の有限値を表します。適切な倍精度数値型をサポートするレスポンス形式は、その型をこのスカラーの表現に利用するべきです。

Result Coercion

Float 型を返すフィールドは倍精度浮動小数点の内部値に遭遇することを期待します。

GraphQL サービスは、情報を失わずに合理的に非浮動小数点の内部値を Float に強制変換できる場合はそれを行えます。そうでない場合は execution error を発生させる必要があります。たとえば整数 11.0 に、文字列 "123"123.0 に変換するなどです。

非有限の浮動小数点値(NaNInfinity)は Float に強制変換できず、execution error を発生させる必要があります。

Input Coercion

入力型として期待される場合、整数および浮動小数点の入力値はどちらも受け入れられます。整数入力値は小数部分を空にして Float に強制変換されます(例: 整数 11.0)。その他の入力値、例えば数値を含む文字列はすべて型不正を示す request error を発生させるべきです。もし入力値が有限 IEEE 754 で表現できない値(例: NaNInfinity、あるいは利用可能な精度の範囲外の値)を表す場合も request error を発生させる必要があります。

3.5.3String

String スカラー型は Unicode コードポイントの列として表現されるテキストデータを表します。String 型は主に自由形式の人間可読テキストを表現するために GraphQL で使われます。内部でどのようにエンコードされるか(例えば UTF-8)はサービス実装に委ねられます。すべてのレスポンスシリアライズ形式は文字列表現(例: JSON の Unicode 文字列)をサポートし、それを用いてこの型をシリアライズしなければなりません。

Result Coercion

String 型を返すフィールドは Unicode 文字列値に遭遇することを期待します。

GraphQL サービスは、情報を失わずに合理的に非文字列の生値を String に強制変換することができます。そうでない場合は execution error を発生させるべきです。例として boolean の true を文字列 "true" に、整数 1 を文字列 "1" に変換することが挙げられます。

Input Coercion

入力型として期待される場合、有効な Unicode 文字列入力値のみが受け入れられます。その他の入力値はすべて型不正を示す request error を発生させるべきです。

3.5.4Boolean

Boolean スカラー型は true または false を表します。レスポンス形式は組み込みのブール型を使うべきで、もし組み込みのブール型がない場合は整数 10 の表現を使うべきです。

Result Coercion

Boolean 型を返すフィールドは boolean の内部値を想定します。

GraphQL サービスは、情報を失わずに合理的に非 boolean の生値を Boolean に強制変換することができます。そうでない場合は execution error を発生させる必要があります。例えば非ゼロの数値を true として返すなどです。

Input Coercion

入力型として期待される場合、真偽値の入力のみが受け入れられます。その他の入力値は型不正を示す request error を発生させるべきです。

3.5.5ID

ID スカラー型は一意識別子を表し、オブジェクトの再取得やキャッシュのキーとしてよく使われます。ID 型は String と同様にシリアライズされますが、必ずしも人間可読を目的とはしていません。数値であることが多いですが、常に String としてシリアライズされなければなりません。

Result Coercion

GraphQL は ID フォーマットに中立であり、様々な形式を跨いで一貫性を保つために文字列へシリアライズします。ID は小さな自動インクリメント数、128 ビットのランダム数、base64 エンコード値、あるいは GUID のような文字列などを表す可能性があります。

GraphQL サービスは期待する ID フォーマットに応じて適切に強制変換すべきです。強制変換が不可能な場合は execution error を発生させるべきです。

Input Coercion

入力型として期待される場合、任意の文字列(例: "4")や整数(例: 4-4)は、そのサービスが期待する ID フォーマットに応じて ID に強制変換されるべきです。その他の入力値、例えば浮動小数点(例: 4.0)は型不正を示す request error を発生させるべきです。

3.5.6スカラー拡張

スカラー型拡張は、以前のスカラー型から拡張されたスカラーを表現するために使用されます。たとえば既存のスカラーにディレクティブを追加するツールやサービスがこれを使うことがあります。

Type Validation

スカラー型拡張は、誤って定義されると無効になる可能性があります。

  1. 拡張対象の名前付き型は既に定義されており、かつスカラー型でなければなりません。
  2. 提供された非繰り返しディレクティブは、以前のスカラー型に既に適用されていてはなりません。

3.6オブジェクト

GraphQL の操作は階層的かつ合成的で、情報のツリーを記述します。スカラー型がこれら階層的操作の葉を記述するのに対し、オブジェクトは中間レベルを記述します。

GraphQL のオブジェクトは、名前付きフィールドの一覧を表し、それぞれのフィールドは特定の型の値を返します。オブジェクト値は、選択されたフィールド名(またはエイリアス)をキー、フィールド評価の結果を値とする順序付きマップとしてシリアライズされるべきであり、その順序は selection set に現れる順序によって決まります。

オブジェクト型内で定義されたすべてのフィールドは、GraphQL のイントロスペクションシステム専用に予約されているため、名前を "__"(アンダースコア二つ)で始めてはなりません。

例えば、型 Person は次のように記述できます:

Example № 44type Person {
  name: String
  age: Int
  picture: Url
}

ここで nameString 値を返すフィールドで、ageInt 値を返すフィールド、pictureUrl 値を返すフィールドです。

オブジェクト値のクエリは少なくとも一つのフィールドを選択しなければなりません。このフィールド選択は、クエリされたオブジェクトの部分集合を正確に含む順序付きマップを生成し、その順序はクエリで指定された順序に従います。宣言されているフィールドのみがそのオブジェクトに対して有効にクエリできます。

例えば Person の全フィールドを選択する場合:

Example № 45{
  name
  age
  picture
}

は次のようなオブジェクトを返します:

Example № 46{
  "name": "Mark Zuckerberg",
  "age": 30,
  "picture": "http://some.cdn/picture.jpg"
}

フィールドのサブセットを選択する場合:

Example № 47{
  age
  name
}

は正確にその部分集合だけを返します:

Example № 48{
  "age": 30,
  "name": "Mark Zuckerberg"
}

オブジェクト型のフィールドは、スカラー、列挙、他のオブジェクト型、インターフェース、またはユニオンであり得ます。さらに、これら五種類のいずれかを基底型とする任意のラップ型であっても構いません。

例えば Person 型は relationship を含むかもしれません:

Example № 49type Person {
  name: String
  age: Int
  picture: Url
  relationship: Person
}

戻り型がオブジェクト型である各フィールドには選択集合(selection set)を指定する必要があるため、次の操作は有効ではありません:

Counter Example № 50{
  name
  relationship
}

しかし、次の例は有効です:

Example № 51{
  name
  relationship {
    name
  }
}

そしてこれは各オブジェクト型のクエリされた部分集合を返します:

Example № 52{
  "name": "Mark Zuckerberg",
  "relationship": {
    "name": "Priscilla Chan"
  }
}
フィールドの順序

オブジェクトをクエリする際、結果として得られるフィールドのマッピングは、実行中に出現した順序と概念的に一致します。ただし、型が適用されないフラグメントや @skip@include ディレクティブでスキップされたフィールドやフラグメントは除外されます。この順序は CollectFields() アルゴリズムを使うことで正しく生成されます。

順序付きマップを表現できるレスポンスのシリアライズ形式は、この順序を維持するべきです。順序付きマップを表現できない形式(例えば JSON)の場合でも、テキスト的にこの順序を保つべきです。つまり、もし二つのフィールド {foo, bar} がその順でクエリされたなら、生成される JSON シリアライズは同じ順序で {"foo": "...", "bar": "..."} を含むべきです。

リクエスト内で現れる順序と同じ順でフィールドを表現することは、デバッグ時の可読性を高め、プロパティの順序を予測できればレスポンスの効率的な解析を可能にします。

フラグメントが他のフィールドより先にスプレッドされている場合、そのフラグメントが指定するフィールドは後続のフィールドより前にレスポンス内に現れます。

Example № 53{
  foo
  ...Frag
  qux
}

fragment Frag on Query {
  bar
  baz
}

は順序付きの結果を生成します:

Example № 54{
  "foo": 1,
  "bar": 2,
  "baz": 3,
  "qux": 4
}

もし同じフィールドが選択内で複数回クエリされた場合、その順序は最初に出現した時点によって決まります。ただし、型が適用されないフラグメントは順序に影響を与えません。

Example № 55{
  foo
  ...Ignored
  ...Matching
  bar
}

fragment Ignored on UnknownType {
  qux
  baz
}

fragment Matching on Query {
  bar
  qux
  foo
}

は次の順序付き結果を生成します:

Example № 56{
  "foo": 1,
  "bar": 2,
  "qux": 3
}

また、ディレクティブによってフィールドが除外される場合、それらはフィールドの順序に考慮されません。

Example № 57{
  foo @skip(if: true)
  bar
  foo
}

は次の順序付き結果を生成します:

Example № 58{
  "bar": 1,
  "foo": 2
}
結果の強制変換

オブジェクトを強制変換した結果を決定することは GraphQL 実行機の中心的処理です。詳しくは Value Completion を参照してください。

入力の強制変換

オブジェクトは入力としては決して有効ではありません。

型の検証

オブジェクト型は不適切に定義されると無効になり得ます。以下のルールは、GraphQL スキーマ内のすべてのオブジェクト型が従うべきものです。

  1. オブジェクト型は 1 つ以上のフィールドを定義していなければなりません。
  2. オブジェクト型の各フィールドについて:
    1. そのフィールドは当該オブジェクト型内で一意の名前を持っていなければなりません。同じ名前のフィールドが二つ存在してはなりません。
    2. そのフィールドの名前は "__"(アンダースコア二つ)で始まってはなりません。
    3. そのフィールドは、IsOutputType(fieldType)true を返す型を返さなければなりません。
    4. そのフィールドの各引数について:
      1. 引数名は "__"(アンダースコア二つ)で始まってはなりません。
      2. 引数名はそのフィールド内で一意でなければなりません。同じ名前の引数が二つあってはなりません。
      3. 引数は、IsInputType(argumentType)true を返す型を受け入れなければなりません。
      4. もし引数の型が Non-Null でありデフォルト値が定義されていない場合:
        1. @deprecated ディレクティブはこの引数に適用してはなりません。
      5. もし引数にデフォルト値がある場合、そのデフォルト値は当該 argumentType に対する強制変換ルールと互換でなければなりません。
  3. オブジェクト型は、1 つ以上の一意なインターフェースを実装すると宣言してよい。
  4. オブジェクト型は、実装すると宣言したすべてのインターフェースのスーパーセットでなければなりません:
    1. このオブジェクト型を objectType とします。
    2. 宣言された各インターフェース interfaceType について、IsValidImplementation(objectType, interfaceType)true でなければなりません。
IsValidImplementation(type, implementedType)
  1. もし implementedType がさらに他のインターフェースを実装すると宣言しているなら、type も同じインターフェースを実装すると宣言していなければなりません。
  2. typeimplementedType 内で定義されている各フィールドと同名のフィールドを含んでいなければなりません。
    1. その同名フィールドを field とします(type 上)。
    2. その同名フィールドを implementedField とします(implementedType 上)。
    3. field は、implementedField に定義された各引数と同名の引数を含まなければなりません。
      1. その同名引数は implementedField 上の同名引数と同じ型(不変)を受け入れなければなりません。
    4. fieldimplementedField に定義されていない追加の引数を含んでも構いませんが、追加引数は必須であってはなりません。つまり Non-Null 型であってはなりません。
    5. field は、implementedField の戻り型と比較して同じ型か、または共変的にその戻り型のサブタイプでなければなりません:
      1. fieldTypefield の戻り型とします。
      2. implementedFieldTypeimplementedField の戻り型とします。
      3. IsValidImplementationFieldType(fieldType, implementedFieldType)true でなければなりません。
    6. もし field が非推奨(deprecated)であるなら、implementedField も非推奨でなければなりません。
IsValidImplementationFieldType(fieldType, implementedFieldType)
  1. もし fieldType が Non-Null 型であるなら:
    1. nullableTypefieldType のアンラップされた nullable 型とします。
    2. もし implementedFieldType が Non-Null 型であれば、そのアンラップされた nullable 型を implementedNullableType とし、そうでなければ implementedFieldType 自身を implementedNullableType とします。
    3. IsValidImplementationFieldType(nullableType, implementedNullableType) を返します。
  2. もし fieldType が List 型であり、かつ implementedFieldType も List 型であるなら:
    1. itemTypefieldType のアンラップされた項目型とします。
    2. implementedItemTypeimplementedFieldType のアンラップされた項目型とします。
    3. IsValidImplementationFieldType(itemType, implementedItemType) を返します。
  3. それ以外の場合、IsSubType(fieldType, implementedFieldType) を返します。
IsSubType(possibleSubType, superType)
  1. もし possibleSubTypesuperType と同一の型であれば true を返します。
  2. もし possibleSubType がオブジェクト型で、かつ superType がユニオン型であり、possibleSubTypesuperType の可能な型の一つであれば true を返します。
  3. もし possibleSubType がオブジェクト型またはインターフェース型で、かつ superType がインターフェース型であり、possibleSubTypesuperType を実装すると宣言しているなら true を返します。
  4. それ以外は false を返します。

3.6.1フィールド引数

オブジェクトのフィールドは概念的に値を生成する関数です。時にフィールドは返す値をさらに指定するための引数を受け取ることができます。オブジェクトフィールドの引数は、可能なすべての引数名と期待される入力型の一覧として定義されます。

フィールド内で定義されるすべての引数名は、GraphQL のイントロスペクションシステム専用に予約されているため、名前を "__"(アンダースコア二つ)で始めてはなりません。

例えば、Person 型の picture フィールドは、返す画像のサイズを決定するための引数を受け取ることができます。

Example № 59type Person {
  name: String
  picture(size: Int): Url
}

操作はこれらのフィールド引数を任意で指定してフィールドに値を渡すことができます。

この操作の例:

Example № 60{
  name
  picture(size: 600)
}

は次の結果を返すかもしれません:

Example № 61{
  "name": "Mark Zuckerberg",
  "picture": "http://some.cdn/picture_600.jpg"
}

オブジェクトフィールドの引数の型は入力型でなければなりません(オブジェクト、インターフェース、ユニオンは除外)。

3.6.2フィールドの非推奨化

アプリケーションの判断により、オブジェクト内のフィールドは非推奨(deprecated)としてマークできます。既存クライアントの互換性を壊さないために、これらのフィールドを選択集合に含めることは依然合法ですが、ドキュメントやツールでは適切に扱われるべきです。

型システム定義言語を使用する場合、フィールドが非推奨であることを示すために @deprecated ディレクティブが使われます:

Example № 62type ExampleType {
  oldField: String @deprecated
}

3.6.3オブジェクト拡張

オブジェクト型拡張は、以前の型から拡張された型を表現するために使用されます。例えばローカルデータを表現するためや、別の GraphQL サービスの拡張として機能するサービスで使われます。

この例では、Story 型にローカルのデータフィールドが追加されています:

Example № 63extend type Story {
  isHiddenLocally: Boolean
}

オブジェクト型拡張は追加フィールドを持たず、代わりにインターフェースやディレクティブのみを追加することも選べます。

この例では、User 型にフィールドを追加せずディレクティブだけを加えています:

Example № 64extend type User @addedDirective
型の検証

オブジェクト型拡張は、不適切に定義されると無効になる可能性があります。

  1. 拡張する対象の名前付き型は既に定義されており、かつオブジェクト型でなければなりません。
  2. オブジェクト型拡張のフィールドは一意の名前でなければなりません。同じ名前のフィールドが二つ存在してはなりません。
  3. オブジェクト型拡張のフィールドは、以前のオブジェクト型ですでに定義されているフィールドであってはなりません。
  4. 提供される非繰り返しディレクティブは、以前のオブジェクト型に既に適用されていてはなりません。
  5. 提供されるインターフェースは、以前のオブジェクト型が既に実装しているものであってはなりません。
  6. 拡張後のオブジェクト型は、実装するすべてのインターフェースのスーパーセットでなければなりません。

3.7インターフェース

GraphQL のインターフェースは、名前付きフィールドとその引数の一覧を表します。GraphQL のオブジェクトやインターフェースはこれらのインターフェースを実装することができ、実装する側の型はそのインターフェースで定義されたすべてのフィールドを定義する必要があります。

インターフェース上のフィールドは、GraphQL オブジェクト上のフィールドと同じ規則に従います。フィールドの型は Scalar、Object、Enum、Interface、または Union のいずれか、もしくはそれら五つのいずれかを基底型とするラップ型であり得ます。

例えば、インターフェース NamedEntity は必須フィールドを記述でき、PersonBusiness のような型がこのインターフェースを実装することで、そのフィールドが常に存在することを保証できます。

型は複数のインターフェースを実装することもできます。例えば下の例では BusinessNamedEntityValuedEntity の両方を実装しています。

Example № 65interface 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
}

インターフェースを返すフィールドは、複数のオブジェクト型のうちどれかが返されることが期待される場合に有用で、しかし保証されるべき共通のフィールドもある場合に使われます。

この例を続けると、ContactNamedEntity を参照するようにできます。

Example № 66type Contact {
  entity: NamedEntity
  phoneNumber: String
  address: String
}

これにより Contact に対して共通フィールドを選択するための selection set を書くことができます。

Example № 67{
  entity {
    name
  }
  phoneNumber
}

インターフェース型上でフィールドを選択する際は、そのインターフェースで宣言されているフィールドのみをクエリできます。上の例では entityNamedEntity を返し、nameNamedEntity 上で定義されているため有効です。しかし、以下のような選択集合は Contact に対しては有効ではありません:

Counter Example № 68{
  entity {
    name
    age
  }
  phoneNumber
}

なぜなら entityNamedEntity を指しており、age はそのインターフェース上で定義されていないからです。age をクエリすることは、entity の結果が Person である場合のみ有効です。これはフラグメントまたはインラインフラグメントを用いて表現できます:

Example № 69{
  entity {
    name
    ... on Person {
      age
    }
  }
  phoneNumber
}
インターフェースがインターフェースを実装する場合

あるインターフェースが別のインターフェースを実装するよう定義する場合、実装するインターフェースは実装される側のインターフェースで指定されている各フィールドを定義しなければなりません。例えば、インターフェース ResourceNode を実装するには id フィールドを定義する必要があります:

Example № 70interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

遷移的に実装されるインターフェース(実装対象のインターフェースがさらに実装しているインターフェース)も、実装する型またはインターフェース上できちんと定義されていなければなりません。例えば ImageResource を実装する際に Node も同時に実装していなければなりません。

Example № 71interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

interface Image implements Resource & Node {
  id: ID!
  url: String
  thumbnail: String
}

インターフェース定義は循環参照を含んではならず、自身を実装してはなりません。次の例は不正です。なぜなら NodeNamed が互いに、また自分自身を実装しているからです:

Counter Example № 72interface Node implements Named & Node {
  id: ID!
  name: String
}

interface Named implements Node & Named {
  id: ID!
  name: String
}
結果の強制変換

インターフェース型は、与えられた結果がどのオブジェクトに対応するかを判定する手段を持つべきです。それが判明したら、インターフェースの結果強制変換はそのオブジェクトの結果強制変換と同じになります。

入力の強制変換

インターフェースは入力としては決して有効ではありません。

型の検証

インターフェース型は不適切に定義されると無効になる可能性があります。

  1. インターフェース型は 1 個以上のフィールドを定義していなければなりません。
  2. インターフェース型の各フィールドについて:
    1. そのフィールドは当該インターフェース型内で一意の名前を持っていなければなりません。同じ名前のフィールドが二つ存在してはなりません。
    2. そのフィールドの名前は "__"(アンダースコア二つ)で始まってはなりません。
    3. そのフィールドは IsOutputType(fieldType)true を返す型を返さなければなりません。
    4. そのフィールドの各引数について:
      1. 引数名は "__"(アンダースコア二つ)で始まってはなりません。
      2. 引数名はそのフィールド内で一意でなければなりません。同じ名前の引数が二つあってはなりません。
      3. 引数は IsInputType(argumentType)true を返す型を受け入れなければなりません。
  3. インターフェース型は一意な一つ以上のインターフェースを実装すると宣言できるが、自己実装はできません。
  4. インターフェース型は、実装するすべてのインターフェースのスーパーセットでなければなりません:
    1. このインターフェース型を implementingType とします。
    2. 宣言された各インターフェース implementedType について、IsValidImplementation(implementingType, implementedType)true でなければなりません。

3.7.1インターフェース拡張

インターフェース型拡張は、以前のインターフェースから拡張されたインターフェースを表現するために使用されます。例えば、多くの型に共通するローカルデータを表現するためや、別の GraphQL サービスの拡張として機能するサービスで使われます。

この例では、拡張されたデータフィールドが NamedEntity 型に追加され、それを実装する型にも追加されています:

Example № 73extend interface NamedEntity {
  nickname: String
}

extend type Person {
  nickname: String
}

extend type Business {
  nickname: String
}

インターフェース型拡張は追加フィールドを持たず、代わりにディレクティブのみを追加することも選べます。

この例では、フィールドを追加せずに NamedEntity 型にディレクティブだけを加えています:

Example № 74extend interface NamedEntity @addedDirective
型の検証

インターフェース型拡張は、不適切に定義されると無効になる可能性があります。

  1. 拡張対象の名前付き型は既に定義されており、かつインターフェース型でなければなりません。
  2. インターフェース型拡張のフィールドは一意の名前でなければなりません。同じ名前のフィールドが二つ存在してはなりません。
  3. インターフェース型拡張のフィールドは、以前のインターフェース型ですでに定義されているフィールドであってはなりません。
  4. 以前のインターフェース型を実装していた任意のオブジェクト型またはインターフェース型は、インターフェース型拡張のフィールドのスーパーセットでなければなりません(これはオブジェクト型拡張によっても満たされ得ます)。
  5. 提供される非繰り返しディレクティブは、以前のインターフェース型に既に適用されていてはなりません。
  6. 拡張後のインターフェース型は、実装するすべてのインターフェースのスーパーセットでなければなりません。

3.8ユニオン

GraphQL のユニオンは、複数の GraphQL オブジェクト型のうちのいずれかであり得るオブジェクトを表しますが、これらの型間で共通して保証されるフィールドはありません。ユニオンはインターフェースと異なり、オブジェクト型は自分が実装するインターフェースを宣言しますが、自分がどのユニオンに含まれるかを知ることはありません。

インターフェースやオブジェクトの場合、直接クエリできるのはその型で定義されているフィールドのみです。インターフェース上の他のフィールドをクエリするには型指定されたフラグメントを使用する必要があります。ユニオンでも同様ですが、ユニオン自体はフィールドを定義しないため、型絞り込みのフラグメントやインラインフラグメントを使わない限り(メタフィールド __typename を除く)この型上のフィールドをクエリすることはできません。

例えば、次のような型を定義できます:

Example № 75union SearchResult = Photo | Person

type Person {
  name: String
  age: Int
}

type Photo {
  height: Int
  width: Int
}

type SearchQuery {
  firstSearchResult: SearchResult
}

この例では、結果が Person であれば name を、Photo であれば height を欲しいというクエリ操作が考えられます。しかしユニオン自体はフィールドを定義しないため、これは曖昧になり無効です。

Counter Example № 76{
  firstSearchResult {
    name
    height
  }
}

有効な操作は型付きフラグメント(この例ではインラインフラグメント)を含みます:

Example № 77{
  firstSearchResult {
    ... on Person {
      name
    }
    ... on Photo {
      height
    }
  }
}

ユニオンのメンバーは、より長い可能な型の一覧を表現する際の書式補助として先頭に省略可能な | 文字を付けて定義できます:

Example № 78union SearchResult =
  | Photo
  | Person
結果の強制変換

ユニオン型は、与えられた結果がどのオブジェクトに対応するかを判定する手段を持つべきです。それが判明したら、ユニオンの結果強制変換はそのオブジェクトの結果強制変換と同じになります。

入力の強制変換

ユニオンは入力としては決して有効ではありません。

型の検証

ユニオン型は不適切に定義されると無効になる可能性があります。

  1. ユニオン型は 1 つ以上の一意なメンバー型を含めなければなりません。
  2. ユニオンのメンバー型はすべてオブジェクトの基底型でなければなりません。スカラー、インターフェース、ユニオン型はユニオンのメンバーになってはなりません。同様に、ラップ型もユニオンのメンバーになってはなりません。

3.8.1ユニオン拡張

ユニオン型拡張は、以前のユニオン型から拡張されたユニオンを表現するために使用されます。例えば、追加のローカルデータを表現するためや、別の GraphQL サービスの拡張として機能するサービスで使われます。

型の検証

ユニオン型拡張は、不適切に定義されると無効になる可能性があります。

  1. 拡張対象の名前付き型は既に定義されており、かつユニオン型でなければなりません。
  2. ユニオン型拡張のメンバー型はすべてオブジェクトの基底型でなければなりません。スカラー、インターフェース、ユニオン型はユニオンのメンバーになってはなりません。同様に、ラップ型もユニオンのメンバーになってはなりません。
  3. ユニオン型拡張のすべてのメンバー型は一意でなければなりません。
  4. ユニオン型拡張のメンバー型は、以前のユニオン型のメンバーであってはなりません。
  5. 提供される非繰り返しディレクティブは、以前のユニオン型に既に適用されていてはなりません。

3.9列挙型

GraphQL の列挙型は、スカラー型と同様に型システムの葉の値を表しますが、列挙型は取りうる値の集合を記述します。

列挙型は数値の参照ではなく、それ自体が一意の値です。シリアライズ時には、表現される値の名前として文字列になる場合があります。

この例では Direction という列挙型が定義されています:

Example № 79enum Direction {
  NORTH
  EAST
  SOUTH
  WEST
}
結果の強制変換

GraphQL サービスは定義された可能な値のうちのいずれかを返さなければなりません。合理的な強制変換ができない場合は execution error を発生させる必要があります。

入力の強制変換

GraphQL には列挙入力値を表す定数リテラルがあります。GraphQL の文字列リテラルは列挙の入力として受け入れてはならず、その場合は request error を発生させるべきです。

EDN のように非文字列の象徴的値を別表現とする変数輸送シリアライゼーション(例えば EDN)では、そのような値のみを列挙入力値として許可すべきです。その他の多くのトランスポートシリアライゼーションでは文字列が列挙入力値と解釈され得るため、文字列は同名の列挙入力値として扱われることがあります。

型の検証

列挙型は不適切に定義されると無効になる可能性があります。

  1. 列挙型は 1 つ以上の一意な列挙値を定義していなければなりません。

3.9.1列挙型拡張

列挙型拡張は、以前の列挙型から拡張された列挙型を表現するために使用されます。例えば、追加のローカルデータを表現するためや、別の GraphQL サービスの拡張として機能するサービスで使われます。

型の検証

列挙型拡張は、不適切に定義されると無効になる可能性があります。

  1. 拡張対象の名前付き型は既に定義されており、かつ列挙型でなければなりません。
  2. 列挙型拡張のすべての値は一意でなければなりません。
  3. 列挙型拡張のすべての値は、以前の列挙の値であってはなりません。
  4. 提供される非繰り返しディレクティブは、以前の列挙型に既に適用されていてはなりません。

3.10入力オブジェクト

フィールドはその動作を構成するために引数を受け取ることがあります。これらの入力は多くの場合スカラーや列挙型ですが、時により複雑な値を表現する必要があります。

GraphQL の Input Object は一連の入力フィールドを定義します。入力フィールドはスカラー、列挙、他の入力オブジェクト、またはそれら三つのいずれかを基底型とするラップ型であり得ます。これにより引数は任意に複雑な構造体を受け取ることが可能になります。

この例では、Input Object Point2Dxy の入力を記述しています。

Example № 80input Point2D {
  x: Float
  y: Float
}
Note 上で定義した GraphQL の Object 型(ObjectTypeDefinition)はここで再利用するのに適切ではありません。なぜなら Object 型は引数を定義するフィールドやインターフェースやユニオンへの参照を含め得ますが、これらは入力引数として使用するのに適切ではないからです。この理由から、入力オブジェクトはシステム内で別個の型として扱われます。
循環参照

入力オブジェクトは他の入力オブジェクトをフィールド型として参照することが許容されます。循環参照は入力オブジェクトが直接的に、あるいは参照を経由して自身を参照する場合に発生します。

循環参照は一般に許容されますが、Non-Null の単数フィールドが途切れなく連鎖する形で定義されてはなりません。そのような入力オブジェクトは合法的な値を提供する方法がないため無効です。

次の循環参照する入力型の例は有効です。フィールド self を省略できるか、値が null になり得るためです。

Example № 81input Example {
  self: Example
  value: String
}

次の例も有効です。self フィールドが空の List になり得るためです。

Example № 82input Example {
  self: [Example!]!
  value: String
}

次の循環参照する入力型の例は無効です。フィールド self に有限な値を提供できないからです。

Counter Example № 83input Example {
  value: String
  self: Example!
}

次の例も無効です。First.secondSecond.first を介した非 null の単数の循環参照が存在するためです。

Counter Example № 84input First {
  second: Second!
  value: String
}

input Second {
  first: First!
  value: String
}
結果の強制変換

入力オブジェクトは結果として有効ではありません。Input Object 型は Object や Interface のフィールドの戻り型にはなり得ません。

入力の強制変換

入力オブジェクトの値は入力オブジェクトリテラルか、変数によって渡された順不同マップであるべきです。そうでない場合は request error が発生しなければなりません。いずれの場合でも、入力オブジェクトリテラルや順不同マップはこの入力オブジェクト型で定義されていない名前のエントリを含んではならず、含んでいる場合はリクエストエラーが発生しなければなりません。

強制変換の結果は、入力オブジェクト型で定義され、かつ値が存在する各フィールドに対するエントリを持つ順不同マップです。結果のマップは以下のルールで構築されます:

  • 定義された入力オブジェクトフィールドに対して値が提供されず、そのフィールド定義がデフォルト値を提供している場合は、当該フィールド型の強制変換ルールに従ってデフォルト値を強制変換した結果を使用するべきです。デフォルト値が提供されておらず、入力オブジェクトフィールドの型が non-null の場合はエラーを発生させるべきです。その他の場合、フィールドが必須でなければ、強制変換された順不同マップにエントリは追加されません。
  • もし入力オブジェクトフィールドに対して明示的に値 null が提供され、そのフィールドの型が non-null でない場合、強制変換された順不同マップのエントリには null が与えられます。つまり、明示的に null を提供するのと、値を提供しないのとでは意味が異なります。
  • もし入力オブジェクトフィールドにリテラル値が提供された場合、そのフィールド型の入力強制変換ルールに従ってその値を強制変換した結果が強制変換された順不同マップのエントリとして与えられます。
  • もし入力オブジェクトフィールドに変数が提供された場合は、その変数の実行時値を使用しなければなりません。実行時値が null でありフィールド型が non-null の場合は execution error が発生しなければなりません。実行時値が提供されていない場合は、変数定義のデフォルト値を使用します。変数定義がデフォルト値を提供していない場合は、入力オブジェクトフィールド定義のデフォルト値を使用します。

以下は、String 型フィールド a と 必須の Int! 型フィールド b を持つ入力オブジェクト型に対する入力強制変換の例です:

Example № 85input 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 は非 null でなければなりません。
{ b: $var } { var: null } エラー: b は非 null でなければなりません。
{ b: 123, c: "xyz" } {} エラー: 予期しないフィールド c
型の検証
  1. Input Object 型は 1 個以上の入力フィールドを定義していなければなりません。
  2. Input Object 型の各入力フィールドについて:
    1. 入力フィールドはその Input Object 型内で一意の名前を持っていなければなりません。同じ名前の入力フィールドが二つ存在してはなりません。
    2. 入力フィールドの名前は "__"(アンダースコア二つ)で始まってはなりません。
    3. 入力フィールドは、IsInputType(inputFieldType)true を返す型を受け入れなければなりません。
    4. もし入力フィールドの型が Non-Null でありデフォルト値が定義されていない場合:
      1. @deprecated ディレクティブはこの入力フィールドに適用してはなりません。
    5. もし Input Object が OneOf Input Object である場合は:
      1. 入力フィールドの型は nullable でなければなりません。
      2. 入力フィールドはデフォルト値を持ってはなりません。
  3. もし Input Object が直接または参照を通して自身を参照する場合、参照の連鎖に含まれるフィールドの少なくとも一つは nullable か List 型でなければなりません。
  4. InputObjectDefaultValueHasCycle(inputObject)false でなければなりません。
InputObjectDefaultValueHasCycle(inputObject, defaultValue, visitedFields)
  1. もし defaultValue が提供されていなければ、空の順不同マップで初期化します。
  2. もし visitedFields が提供されていなければ、空の集合で初期化します。
  3. もし defaultValue がリストであれば:
    1. defaultValue 内の各 itemValue に対して:
      1. もし InputObjectDefaultValueHasCycle(inputObject, itemValue, visitedFields) が真であれば、true を返します。
  4. それ以外で、もし defaultValue が順不同マップであれば:
    1. inputObject の各フィールド field に対して:
      1. もし InputFieldDefaultValueHasCycle(field, defaultValue, visitedFields) が真であれば、true を返します。
  5. false を返します。
InputFieldDefaultValueHasCycle(field, defaultValue, visitedFields)
  1. 断言: defaultValue は順不同マップである。
  2. fieldTypefield の型とします。
  3. namedFieldTypefieldType の基底の名前付き型とします。
  4. もし namedFieldType が入力オブジェクト型でなければ:
    1. false を返します。
  5. fieldNamefield の名前とします。
  6. fieldDefaultValuedefaultValue 内の fieldName に対する値とします。
  7. もし fieldDefaultValue が存在する場合:
    1. InputObjectDefaultValueHasCycle(namedFieldType, fieldDefaultValue, visitedFields) を返します。
  8. それ以外の場合:
    1. fieldDefaultValuefield のデフォルト値とします。
    2. もし fieldDefaultValue が存在しなければ:
      1. false を返します。
    3. もし fieldvisitedFields に含まれている場合:
      1. true を返します。
    4. nextVisitedFieldsfieldvisitedFields のすべてを含む新しい集合とします。
    5. InputObjectDefaultValueHasCycle(namedFieldType, fieldDefaultValue, nextVisitedFields) を返します。

3.10.1OneOf 入力オブジェクト

OneOf Input Object は Input Object の特別な変種で、ちょうど一つのフィールドが設定されかつ非 null でなければならず、他のフィールドは省略されている必要があります。これは入力が多くの選択肢のうちのいずれかであることを表現する場合に便利です。

型システム定義言語を使用する際、Input Object が OneOf Input Object であることを示すために @oneOf ディレクティブが使用されます(これによりちょうど一つのフィールドが提供されることが要求されます):

input UserUniqueCondition @oneOf {
  id: ID
  username: String
  organizationAndEmail: OrganizationAndEmailInput
}

スキーマイントロスペクションにおいて、__Type.isOneOf フィールドは OneOf Input Objects に対して true を返し、それ以外の Input Objects に対しては false を返します。

入力の強制変換

OneOf Input Object の値も Input Object の変種として、入力オブジェクトリテラルか変数によって与えられた順不同マップでなければなりません。そうでない場合は request error を発生させる必要があります。

  • 強制変換による順不同マップの構築の前に: 強制対象の値はちょうど一つのエントリを含み、そのエントリは nullまたは null リテラルであってはなりません。そうでない場合は request error を発生させる必要があります。
  • すべての Input Object の入力強制変換ルールは OneOf Input Object にも適用されます。
  • 得られた強制変換後のマップはちょうど一つのエントリを含み、そのエントリの値は null ではあってはなりません。そうでない場合は execution error を発生させる必要があります。

以下は、メンバフィールド aString)と bInt)を持つ OneOf Input Object の入力強制変換の追加例です:

Example № 86input ExampleOneOfInputObject @oneOf {
  a: String
  b: Int
}
リテラル値 変数 強制変換後の値
{ a: "abc" } {} { a: "abc" }
{ b: 123 } {} { b: 123 }
$var { var: { a: "abc" } } { a: "abc" }
{ a: null } {} エラー: メンバフィールド a の値は非 null でなければなりません
$var { var: { a: null } } エラー: メンバフィールド a の値は非 null でなければなりません
{ a: $a } {} エラー: メンバフィールド a の値が指定されている必要があります
{ a: "abc", b: 123 } {} エラー: 正確に一つのキーを指定する必要があります
{ a: 456, b: "xyz" } {} エラー: 正確に一つのキーを指定する必要があります
$var { var: { a: "abc", b: 123 } } エラー: 正確に一つのキーを指定する必要があります
{ a: "abc", b: null } {} エラー: 正確に一つのキーを指定する必要があります
{ a: "abc", b: $b } {} エラー: 正確に一つのキーを指定する必要があります
{ a: $a, b: $b } { a: "abc" } エラー: 正確に一つのキーを指定する必要があります
{} {} エラー: 正確に一つのキーを指定する必要があります
$var { var: {} } エラー: 正確に一つのキーを指定する必要があります

3.10.2入力オブジェクト拡張

入力オブジェクト型拡張は、以前の入力オブジェクト型から拡張された入力オブジェクト型を表すために使用されます。例えば、別の GraphQL サービスの拡張として機能するサービスがこれを用いることがあります。

型の検証

入力オブジェクト型拡張は、不適切に定義されると無効になる可能性があります。

  1. 拡張対象の名前付き型は既に定義されており、かつ Input Object 型でなければなりません。
  2. 入力オブジェクト型拡張のすべてのフィールドは一意の名前でなければなりません。
  3. 入力オブジェクト型拡張のすべてのフィールドは、以前の入力オブジェクトのフィールドであってはなりません。
  4. 提供される非繰り返しディレクティブは、以前の入力オブジェクト型にすでに適用されていてはなりません。
  5. @oneOf ディレクティブは入力オブジェクト型拡張によって提供してはなりません。
  6. もし元の Input Object が OneOf Input Object である場合は:
    1. 入力オブジェクト型拡張のすべてのフィールドは nullable でなければなりません。
    2. 入力オブジェクト型拡張のすべてのフィールドはデフォルト値を持ってはなりません。

3.11リスト

GraphQL のリストは、リスト内の各項目の型(リストの項目型と呼ばれる)を宣言する特殊なコレクション型です。リスト値は順序付きリストとしてシリアライズされ、リスト内の各項目は項目型に従ってシリアライズされます。

フィールドがリスト型を使用することを示すには、項目型を角括弧で包みます:pets: [Pet] のように。リストの入れ子も許容されます:matrix: [[Int]]

結果の強制変換

GraphQL サービスはリスト型の結果として順序付きリストを返さなければなりません。リスト内の各項目は項目型に対する結果強制変換の結果でなければなりません。合理的な強制変換ができない場合は 実行エラー を発生させる必要があります。特に、非リストが返された場合は型システムと実装の期待が一致していないことを示すため強制変換は失敗すべきです。

リストの項目型が nullable の場合、個々の項目の準備または強制変換中に発生したエラーは、その位置の値を null にし、レスポンスに 実行エラー を追加する結果になります。リストの項目型が non-null の場合、個々の項目で発生した実行エラーはリスト全体に対する実行エラーとなる必要があります。

詳細な振る舞いについては 実行エラーの処理 を参照してください。
入力の強制変換

入力として期待される場合、リスト値はリストの項目型が受け入れ可能な各項目を持つ場合にのみ受け入れられます。

もしリスト型への入力として渡された値がリストではなく、かつ nullではない場合、入力強制変換の結果は要素数 1 のリストとなり、単一の項目値は提供された値に対してリストの項目型の入力強制変換を行った結果になります(ネストしたリストに対しては再帰的に適用され得ます)。

これにより、可変長引数(しばしば "var args" と呼ばれる)を受け取る入力はリスト型として宣言しつつ、単一値の場合はクライアントがリストを構築せず直接その値を渡せるようになります。

以下は様々なリスト型と値に対する入力強制変換の例です:

期待される型 提供された値 強制変換後の値
[Int] [1, 2, 3] [1, 2, 3]
[Int] [1, "b", true] エラー: 不正な項目の値
[Int] 1 [1]
[Int] null null
[[Int]] [[1], [2, 3]] [[1], [2, 3]]
[[Int]] [1, 2, 3] [[1], [2], [3]]
[[Int]] [1, null, 3] [[1], null, [3]]
[[Int]] [[1], ["b"]] エラー: 不正な項目の値
[[Int]] 1 [[1]]
[[Int]] null null

3.12Non-Null(非 null)

デフォルトでは、GraphQL のすべての型は nullable です。上記の型に対して null 値は有効なレスポンスです。null を許容しない型を宣言するには GraphQL の Non-Null 型を使用します。この型は基底の型をラップし、そのラップ型は基底型と同一の振る舞いをしますが、ラップ型に対して null は有効なレスポンスではない点が異なります。トレイリングの感嘆符で Non-Null を示します:name: String!.

Nullable と Optional

フィールドは 常に 選択集合(selection set)の文脈内ではオプションです。フィールドは省略可能であり、選択集合が空にならない限りその選択集合は有効です。ただし、Non-Null 型を返すフィールドはクエリ時に null を返すことはありません。

入力(フィールド引数など)はデフォルトで常にオプションです。しかし Non-Null の入力型は必須です。Non-Null は null を受け入れないだけでなく、省略も受け入れません。簡略化のため、nullable 型は常にオプション、non-null 型は常に必須と見なされます。

結果の強制変換

上記の結果強制変換では null は有効な値として扱われていました。Non-Null 型の結果を強制変換するには、ラップされた型の強制変換を行います。その結果が null でなければ、その値が Non-Null 型の強制変換結果となります。もしその結果が null であれば、実行エラー を発生させなければなりません。

非 null のレスポンス位置で 実行エラー が発生した場合、そのエラーは親のレスポンス位置へ伝播します。このプロセスの詳細は実行セクション内の Errors and Non-Null Types(エラーと Non-Null 型) を参照してください。
入力の強制変換

もし Non-Null 型の引数または入力オブジェクトフィールドに対して値が提供されていない、文字どおりの値として null が提供された、もしくは実行時に値が提供されていない変数が提供された、あるいは実行時に null が与えられた場合は、リクエストエラー を発生させなければなりません。

もし Non-Null 型に提供された値が null以外のリテラル値、もしくは Non-Null な変数値である場合は、ラップされた型に対する入力強制変換を用いてそれを強制変換します。

非 null の引数は省略できません:

Counter Example № 87{
  fieldWithNonNullArg
}

非 null の引数に null を提供することはできません:

Counter Example № 88{
  fieldWithNonNullArg(nonNullArg: null)
}

nullable 型の変数を非 null の引数に渡すことはできません:

Example № 89query withNullableVariable($var: String) {
  fieldWithNonNullArg(nonNullArg: $var)
}
バリデーション節では、nullable 型の変数を non-null の入力型に提供することは無効であると定義されています。
型の検証
  1. Non-Null 型は別の Non-Null 型をラップしてはなりません。

3.12.1リストと非Nullの組み合わせ

List と Non-Null のラップ型は合成でき、より複雑な型を表現できます。List と Non-Null 型の結果強制変換および入力強制変換の規則は再帰的に適用されます。

例えば、List の内部項目型が Non-Null(例: [T!])であれば、その List は null の項目を含んではなりません。一方、Non-Null の内部型が List(例: [T]!)である場合、null は受け入れられませんが空リストは受け入れられます。

以下は様々な型と値に対する結果強制変換の例です:

期待される型 内部値 強制変換後の結果
[Int] [1, 2, 3] [1, 2, 3]
[Int] null null
[Int] [1, 2, null] [1, 2, null]
[Int] [1, 2, Error] [1, 2, null](エラー記録付き)
[Int]! [1, 2, 3] [1, 2, 3]
[Int]! null エラー: 値は null にできません
[Int]! [1, 2, null] [1, 2, null]
[Int]! [1, 2, Error] [1, 2, null](エラー記録付き)
[Int!] [1, 2, 3] [1, 2, 3]
[Int!] null null
[Int!] [1, 2, null] null(強制変換エラー記録付き)
[Int!] [1, 2, Error] null(エラー記録付き)
[Int!]! [1, 2, 3] [1, 2, 3]
[Int!]! null エラー: 値は null にできません
[Int!]! [1, 2, null] エラー: 項目は null にできません
[Int!]! [1, 2, Error] エラー: 項目でエラーが発生しました

3.13ディレクティブ

ExecutableDirectiveLocation
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
TypeSystemDirectiveLocation
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION

GraphQL スキーマは、ドキュメントの様々な部分に注釈を付けるために使用されるディレクティブを記述します。これによりバリデータ、実行器、あるいはコード生成のようなクライアントツールが異なる評価を行えます。

組み込みディレクティブ

組み込みディレクティブ はこの仕様内で定義されたディレクティブを指します。

GraphQL 実装は @skip@include のディレクティブを提供するべきです。

型システム定義言語をサポートする GraphQL 実装は、スキーマ内の非推奨部分を表すために @deprecated ディレクティブを提供する必要があります。

カスタムスカラー定義を表す場合、型システム定義言語をサポートする実装は @specifiedBy を提供するべきです。

OneOf 入力オブジェクトを表す場合、型システム定義言語をサポートする実装は @oneOf を提供するべきです。

型システム定義言語で GraphQL スキーマを表現する際、簡潔さのために任意の 組み込みディレクティブ を省略できます。

GraphQL サービスをイントロスペクトする際、提供されているすべてのディレクティブ(組み込みディレクティブを含む)は返されるディレクティブ集合に含まれていなければなりません。

カスタムディレクティブ

GraphQL サービスやクライアントツールは、この文書で定義されたもの以外の任意の カスタムディレクティブ を提供できます。ディレクティブは GraphQL をカスタムまたは実験的な振る舞いで拡張する推奨手段です。

カスタムディレクティブ を定義する際は、ディレクティブ名にプレフィックスを付けて利用範囲を明確にし、将来の本仕様で定義され得る組み込みディレクティブ名との衝突を避けることが推奨されます。例えば Facebook のサービスで使うディレクティブは @auth ではなく @fb_auth のように命名することが推奨されます。

ディレクティブは宣言された適用箇所でのみ使用されるべきです。以下の例ではフィールドに注釈を付けるためのディレクティブが定義されています:

Example № 90directive @example on FIELD

fragment SomeFragment on SomeType {
  field @example
}

ディレクティブの適用場所は、長い一覧を表現する際の書式補助として先頭に省略可能な | を含めて定義できます:

Example № 91directive @example on
  | FIELD
  | FRAGMENT_SPREAD
  | INLINE_FRAGMENT

ディレクティブは型システム定義言語の注釈としても使用でき、スキーマやクライアント生成コードのための追加メタデータを提供するのに役立ちます。

以下の例では、@example がフィールド定義と引数定義を注釈しています:

Example № 92directive @example on FIELD_DEFINITION | ARGUMENT_DEFINITION

type SomeType {
  field(arg: Int @example): String @example
}

ディレクティブは repeatable キーワードを含めて繰り返し可能として定義できます。繰り返し可能なディレクティブは、同一箇所で異なる引数とともに同じディレクティブを複数回使いたい場合に有用です:

Example № 93directive @delegateField(name: String!) repeatable on OBJECT | INTERFACE

type Book @delegateField(name: "pageCount") @delegateField(name: "author") {
  id: ID!
}

extend type Book @delegateField(name: "index")

ディレクティブを定義する際、そのディレクティブが直接的または間接的に自身を参照してはなりません:

Counter Example № 94directive @invalidExample(arg: String @invalidExample) on ARGUMENT_DEFINITION
ディレクティブが現れる順序は重要であり得ます。繰り返し可能なディレクティブも同様です。
型の検証
  1. ディレクティブ定義は少なくとも一つの DirectiveLocation を含めなければなりません。
  2. ディレクティブ定義は自身を直接参照するディレクティブの使用を含んではなりません。
  3. ディレクティブ定義は、ある型やディレクティブが転移的にこのディレクティブへの参照を含むことで間接的に自身を参照するような使用を含んではなりません。
  4. ディレクティブ名は "__"(アンダースコア二つ)で始まってはなりません。
  5. ディレクティブの各引数について:
    1. 引数名は "__" で始まってはなりません。
    2. 引数名はそのディレクティブ内で一意でなければなりません。
    3. 引数は IsInputType(argumentType)true を返す型を受け入れなければなりません。

3.13.1@skip

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

@skip 組み込みディレクティブはフィールド、フラグメントスプレッド、インラインフラグメントに対して提供され得ます。if 引数に従って実行時に条件的に除外することを可能にします。

次の例では、変数 $someTestfalse の場合のみ experimentalField がクエリされます。

Example № 95query myQuery($someTest: Boolean!) {
  experimentalField @skip(if: $someTest)
}

3.13.2@include

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

@include 組み込みディレクティブはフィールド、フラグメントスプレッド、インラインフラグメントに対して提供され得ます。if 引数に従って実行時に条件的に包含することを可能にします。

次の例では、変数 $someTesttrue の場合のみ experimentalField がクエリされます。

Example № 96query myQuery($someTest: Boolean!) {
  experimentalField @include(if: $someTest)
}
@skip@include のどちらにも優先権はありません。もし同一フィールドやフラグメントに両方が付与されている場合、そのフィールドやフラグメントは @skip の条件が false かつ @include の条件が true のときのみクエリされなければなりません。逆に言えば、どちらか一方でも(@skip の条件が true、または @include の条件が false)であればクエリされてはなりません。

3.13.3@deprecated

directive @deprecated(
  reason: String! = "No longer supported"
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE

@deprecated 組み込みディレクティブは、型システム定義言語内でスキーマの非推奨部分(型上の非推奨フィールド、フィールドの引数、入力型の入力フィールド、列挙型の値など)を示すために使用されます。

非推奨理由は Markdown(CommonMark)で書式化できます。

次の例では、oldFieldnewField を使うことが推奨され、oldArgnewArg を使うことが推奨されています。

Example № 97type ExampleType {
  newField: String
  oldField: String @deprecated(reason: "Use `newField`.")

  anotherField(
    newArg: String
    oldArg: String @deprecated(reason: "Use `newArg`.")
  ): String
}

@deprecated は必須の引数(デフォルトなしの non-null)や必須の入力オブジェクトフィールドには付与してはなりません。

Counter Example № 98type ExampleType {
  invalidField(
    newArg: String
    oldArg: String! @deprecated(reason: "Use `newArg`.")
  ): String
}

必須の引数や入力フィールドを非推奨にするには、まずその型を nullable に変更するかデフォルト値を追加してオプションにする必要があります。

3.13.4@specifiedBy

directive @specifiedBy(url: String!) on SCALAR

@specifiedBy 組み込みディレクティブは型システム定義言語内でカスタムスカラーの挙動を指定するための scalar specification URL を提供するために使われます。URL はデータ形式、シリアライズ、強制変換ルールの人間可読の仕様を指すべきであり、組み込みスカラーには付与してはなりません。

GraphQL スカラー仕様の実装に関する詳細は scalars.graphql.org implementation guide を参照してください。

次の例では、カスタムスカラー UUID が対応する IETF 仕様への URL とともに定義されています。

Example № 99scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")

3.13.5@oneOf

directive @oneOf on INPUT_OBJECT

@oneOf 組み込みディレクティブは、ある Input ObjectOneOf Input Object であることを示すために型システム定義言語内で使用されます。

Example № 100input UserUniqueCondition @oneOf {
  id: ID
  username: String
  organizationAndEmail: OrganizationAndEmailInput
}

4イントロスペクション

GraphQL サービスはスキーマに関するイントロスペクションをサポートします。このスキーマは GraphQL 自体を用いてクエリされ、ツール構築のための強力なプラットフォームを生み出します。

簡単なアプリの例を考えましょう。この場合、User 型が三つのフィールド(id、name、birthday)を持っています。

例えば、次の型定義を持つサービスがあるとします:

Example № 101type User {
  id: String
  name: String
  birthday: Date
}

次の操作を含むリクエストは:

Example № 102{
  __type(name: "User") {
    name
    fields {
      name
      type {
        name
      }
    }
  }
}

は次の結果を生成します:

Example № 103{
  "__type": {
    "name": "User",
    "fields": [
      {
        "name": "id",
        "type": { "name": "String" }
      },
      {
        "name": "name",
        "type": { "name": "String" }
      },
      {
        "name": "birthday",
        "type": { "name": "Date" }
      }
    ]
  }
}
予約名

ユーザー定義の型やフィールドと同じ文脈で使用される、GraphQL イントロスペクションシステムによって必要とされる型やフィールドには、ユーザー定義の GraphQL 型との名前衝突を避けるために "__"(アンダースコア二つ)が接頭辞として付けられます。

それ以外の、GraphQL 型システム内の任意の Name は、二つのアンダースコア "__" で始まってはなりません。

4.1型名のイントロスペクション

GraphQL は、サブスクリプション操作のルートでの選択を除く任意の selection set 内で型名のイントロスペクションをサポートします。型名のイントロスペクションはメタフィールド __typename: String! を Object、Interface、Union の任意の場所で利用することで行われ、実行時にその位置での具象 Object 型の名前を返します。

これは主に Interface や Union 型に対するクエリ時に、返された実際の Object 型を識別するために使われます。

メタフィールドとして __typename は暗黙的であり、定義済みの型のフィールド一覧には現れません。

__typename はサブスクリプション操作のルートフィールドとして含めることはできません。

4.2スキーマのイントロスペクション

スキーマイントロスペクションシステムは、クエリ操作のルートの型からアクセス可能なメタフィールド __schema__type を通じて利用できます。

__schema: __Schema!
__type(name: String!): __Type

これらはすべてのメタフィールドと同様に暗黙的であり、クエリ操作のルート型のフィールド一覧には現れません。

ファーストクラスのドキュメント

イントロスペクションシステム内のすべての型は description フィールド(型: String)を提供し、型設計者が機能に加えてドキュメントを公開できるようにします。GraphQL サービスは description フィールドに Markdown(CommonMark に準拠)を返すことができます。したがって description を表示するツールは CommonMark 準拠の Markdown レンダラを使用することが推奨されます。

非推奨

後方互換性の管理を支援するために、GraphQL のフィールド、引数、入力フィールド、および列挙値は isDeprecated: Boolean! によって非推奨であるかどうかを示し、なぜ非推奨であるかを説明するために deprecationReason: String を提供できます。

イントロスペクションを用いて構築されたツールは、非推奨の使用を情報の隠蔽や開発者向けの警告を通じて抑止するよう配慮すべきです。

安定した順序

すべてのデータコレクションの観測可能な順序は、スキーマの可読性と安定性を向上させるために保持されるべきです。TypeSystemDocument からスキーマが生成される場合、イントロスペクションはオブジェクトフィールド、入力オブジェクトフィールド、引数、列挙値、ディレクティブ、ユニオンのメンバー型、および実装しているインターフェースといった各要素リストについてソース中の同じ順序で項目を返すべきです。

スキーマイントロスペクションスキーマ

スキーマイントロスペクションシステム自体が GraphQL スキーマとして表現されています。以下はイントロスペクションを提供するための型システム定義の全集合であり、それらは後続の節で完全に定義されています。

type __Schema {
  description: String
  types: [__Type!]!
  queryType: __Type!
  mutationType: __Type
  subscriptionType: __Type
  directives: [__Directive!]!
}

type __Type {
  kind: __TypeKind!
  name: String
  description: String
  # may be non-null for custom SCALAR, otherwise null.
  specifiedByURL: 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(includeDeprecated: Boolean! = false): [__InputValue!]
  # must be non-null for NON_NULL and LIST, otherwise null.
  ofType: __Type
  # must be non-null for INPUT_OBJECT, otherwise null.
  isOneOf: Boolean
}

enum __TypeKind {
  SCALAR
  OBJECT
  INTERFACE
  UNION
  ENUM
  INPUT_OBJECT
  LIST
  NON_NULL
}

type __Field {
  name: String!
  description: String
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
  type: __Type!
  isDeprecated: Boolean!
  deprecationReason: String
}

type __InputValue {
  name: String!
  description: String
  type: __Type!
  defaultValue: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __EnumValue {
  name: String!
  description: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __Directive {
  name: String!
  description: String
  isRepeatable: Boolean!
  locations: [__DirectiveLocation!]!
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
}

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 は型イントロスペクションシステムの中核にあります。これはシステム内のすべての型(名前付き型(例:Scalars や Object 型)および型修飾子(例:List や Non-Null 型))を表します。

型修飾子はフィールド ofType に示される修飾された型を変更するために使われます。この修飾された型は再帰的にさらに修飾され得て、最終的に名前付き型を修飾します(リストや非 null の組合せを表現可能)。

型にはいくつかの異なる種類があり、各種類ごとに有効なフィールドが異なります。すべての可能な種類は __TypeKind 列挙で列挙されています。

以下の各小節では、__TypeKind 列挙の各値に対して __Type の期待されるフィールドを定義します:

  • "SCALAR"
  • "OBJECT"
  • "INTERFACE"
  • "UNION"
  • "ENUM"
  • "INPUT_OBJECT"
  • "LIST"
  • "NON_NULL"
スカラー

Int、String、Boolean のようなスカラー型を表します。スカラーはフィールドを持てません。

また、カスタムスカラー(Custom scalars)も表現し、カスタムスカラーは specifiedByURL を提供する場合があります(scalar specification URL)。

フィールド:

  • kind__TypeKind.SCALAR を返さなければなりません。
  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • specifiedByURL はカスタムスカラーに対して URL 形式の String を返すことがあり、それ以外では null でなければなりません。
  • その他のすべてのフィールドは null を返さなければなりません。
オブジェクト

オブジェクト型は一連のフィールドの具体的な実装を表します。イントロスペクション型(例:__Type__Field 等)はオブジェクトの例です。

フィールド:

  • kind__TypeKind.OBJECT を返さなければなりません。
  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • fields はこの型で選択可能なフィールドの集合を返さなければなりません。
    • 引数 includeDeprecated を受け取り、デフォルトは false です。もし true なら、非推奨のフィールドも返されます。
  • interfaces はそのオブジェクトが実装するインターフェースの集合を返さなければなりません(ない場合は空集合を返します)。
  • その他のすべてのフィールドは null を返さなければなりません。
ユニオン

ユニオンは共通のフィールドを宣言しない抽象型です。ユニオンの可能な型は possibleTypes に明示的に列挙されます。オブジェクト型はそのユニオンのメンバーになり得ますが、当該オブジェクト型自体の修正は不要です。

フィールド:

  • kind__TypeKind.UNION を返さなければなりません。
  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • possibleTypes はこのユニオン内で表現可能な型のリストを返します。それらはオブジェクト型でなければなりません。
  • その他のすべてのフィールドは null を返さなければなりません。
インターフェース

インターフェースは共通フィールドが宣言される抽象型です。インターフェースを実装する任意の型は、そのインターフェースで定義されたすべての名前付きフィールドを定義し、各実装フィールドの型はインターフェース型と同じかサブタイプ(共変)でなければなりません。このインターフェースの実装型は possibleTypes に明示的に列挙されます。

フィールド:

  • kind__TypeKind.INTERFACE を返さなければなりません。
  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • fields はこのインターフェースが要求するフィールドの集合を返さなければなりません。
    • 引数 includeDeprecated を受け取り、デフォルトは false です。もし true なら、非推奨のフィールドも返されます。
  • interfaces はこのインターフェースが実装するインターフェースの集合を返さなければなりません(ない場合は空集合を返します)。
  • possibleTypes はこのインターフェースを実装する型のリストを返します。それらはオブジェクト型でなければなりません。
  • その他のすべてのフィールドは null を返さなければなりません。
列挙型

列挙型は取り得る値の集合のみを持つ特別なスカラーです。

フィールド:

  • kind__TypeKind.ENUM を返さなければなりません。
  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • enumValues__EnumValue のリストとして列挙値の集合を返さなければなりません。少なくとも一つ存在し、名前は一意でなければなりません。
    • 引数 includeDeprecated を受け取り、デフォルトは false です。もし true なら、非推奨の列挙値も返されます。
  • その他のすべてのフィールドは null を返さなければなりません。
入力オブジェクト

入力オブジェクトは名前付き入力値のリストとして定義された複合型です。これらは引数や変数の入力としてのみ使用され、フィールドの返り値の型にはなり得ません。

例えば入力オブジェクト Point は次のように定義できます:

Example № 104input Point {
  x: Int
  y: Int
}

フィールド:

  • kind__TypeKind.INPUT_OBJECT を返さなければなりません。
  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • inputFields__InputValue のリストとして入力フィールドの集合を返さなければなりません。
    • 引数 includeDeprecated を受け取り、デフォルトは false です。もし true なら、非推奨の入力フィールドも返されます。
  • isOneOf は、その入力オブジェクトが OneOf Input Object を表す場合は true を、そうでない場合は false を返さなければなりません。
  • その他のすべてのフィールドは null を返さなければなりません。
リスト

リストは GraphQL における値の列を表します。List 型は型修飾子であり、ofType フィールドで包む別の型インスタンスを指定し、それがリスト内の各項目の型を定義します。

ofType の修飾された型はさらに修飾型であり得るため、リストのリストや非 null のリストなどを表現できます。

フィールド:

  • kind__TypeKind.LIST を返さなければなりません。
  • ofType は任意の種類の型を返さなければなりません。
  • その他のすべてのフィールドは null を返さなければなりません。
Non-Null(非 null)

GraphQL の型はデフォルトで nullable です。フィールド型に対して null が有効なレスポンスです。

Non-Null 型は型修飾子であり、ofType フィールドで別の型インスタンスを包みます。Non-Null 型はレスポンスとして null を許容せず、引数や入力オブジェクトフィールドの必須入力を示します。

ofType の修飾された型はさらにリスト型であり得るため、リストの Non-Null を表現できます。ただし Non-Null の重複(Non-Null of Non-Null)を避けるために、ofType は Non-Null 型を返してはなりません。

フィールド:

  • kind__TypeKind.NON_NULL を返さなければなりません。
  • ofType は Non-Null 以外の任意の種類の型を返さなければなりません。
  • その他のすべてのフィールドは null を返さなければなりません。

4.2.3__Field 型

__Field 型はオブジェクト型またはインターフェース型の各フィールドを表します。

フィールド:

  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • args はこのフィールドが受け取る引数を表す __InputValue のリストを返します。
    • 引数 includeDeprecated を受け取り、デフォルトは false です。もし true なら、非推奨の引数も返されます。
  • type はこのフィールドが返す値の型を表す __Type を返さなければなりません。
  • isDeprecated はこのフィールドがもはや使用されるべきでない場合に true を返し、そうでない場合は false を返します。
  • deprecationReason はなぜこのフィールドが非推奨であるかを示す理由を返すか、非推奨でない場合は null を返します。

4.2.4__InputValue 型

__InputValue 型はフィールドやディレクティブの引数、ならびに入力オブジェクトの inputFields を表します。

フィールド:

  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • type はこの入力値が期待する型を表す __Type を返さなければなりません。
  • defaultValue は、実行時に値が提供されない場合にこの入力値が使うデフォルト値の GraphQL 言語によるエンコード(文字列)を返すことがあります。デフォルト値がない場合は null を返します。
  • isDeprecated はこの入力フィールドや引数がもはや使用されるべきでない場合に true を返し、そうでない場合は false を返します。
  • deprecationReason はこの入力フィールドや引数が非推奨である理由を返すか、非推奨でない場合は null を返します。

4.2.5__EnumValue 型

__EnumValue 型は列挙の可能な値の一つを表します。

フィールド:

  • name は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • isDeprecated はこの列挙値がもはや使用されるべきでない場合に true を返し、そうでない場合は false を返します。
  • deprecationReason はこの列挙値が非推奨である理由を返すか、非推奨でない場合は null を返します。

4.2.6__Directive 型

__Directive 型はサービスがサポートするディレクティブを表します。

これは組み込みディレクティブ(built-in directive)とカスタムディレクティブ(custom 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 は String を返さなければなりません。
  • description は String か null を返すことがあります。
  • locations はこのディレクティブが置かれ得る有効な位置を表す __DirectiveLocation のリストを返します。
  • args はこのディレクティブが受け取る引数を表す __InputValue のリストを返します。
    • 引数 includeDeprecated を受け取り、デフォルトは false です。もし true なら、非推奨の引数も返されます。
  • isRepeatable はそのディレクティブが単一の位置で繰り返し使用可能かを示す Boolean を返さなければなりません。

5検証

GraphQL サービスはリクエストが構文的に正しいかどうかを確認するだけでなく、与えられた GraphQL スキーマの文脈において曖昧さや誤りがないことを保証します。

無効なリクエストは技術的には実行可能であり、実行セクションのアルゴリズムで定義される通り常に安定した結果を生成しますが、その結果は曖昧であったり、驚きを伴ったり、検証エラーを含むリクエストと比較して予期しない場合があるため、実行は有効なリクエストに対してのみ行うべきです。

通常、検証は実行直前にリクエストの文脈で行われますが、GraphQL サービスは同一のリクエストが以前に検証済みであることが分かっている場合は明示的に再検証せずにそのリクエストを実行することができます。例えば:リクエストが開発時に検証され、その後変更されていない場合、あるいはサービスが一度リクエストを検証して結果をメモ化し将来同じリクエストを再検証しないようにする場合などです。クライアント側や開発時のツールは検証エラーを報告し、その時点で無効であることが分かっているリクエストの作成や実行を許可しないようにするべきです。

Type System Evolution

GraphQL 型システムスキーマは、新しい型や新しいフィールドの追加によって時間とともに進化します。そのため以前は有効だったリクエストが後に無効になる可能性があります。以前有効だったリクエストを無効にするような変更は breaking change(互換性を壊す変更)とみなされます。GraphQL サービスやスキーマの保守者は破壊的変更を避けることが推奨されますが、これらの破壊的変更に対してより堅牢であるために、洗練された GraphQL システムは「ある時点で」検証エラーがないことが確認され、以後変更されていないリクエストの実行を許可する場合があります。

Examples

このセクションの例では、以下の型を使用します:

Example № 105type Query {
  dog: Dog
  findDog(searchBy: FindDogInput): Dog
}

type Mutation {
  addPet(pet: PetInput!): Pet
  addPets(pets: [PetInput!]!): [Pet]
}

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

input FindDogInput {
  name: String
  owner: String
}

input CatInput {
  name: String!
  nickname: String
  meowVolume: Int
}

input DogInput {
  name: String!
  nickname: String
  barkVolume: Int
}

input PetInput @oneOf {
  cat: CatInput
  dog: DogInput
}

5.1ドキュメント

5.1.1実行可能定義

形式的仕様
説明文

GraphQL の実行は、実行可能な定義である Operation と Fragment のみを考慮します。型システムの定義や拡張は実行可能ではなく、実行時には考慮されません。

曖昧さを避けるために、TypeSystemDefinitionOrExtension を含むドキュメントは実行のためには無効です。

直接実行することを意図していない GraphQL ドキュメントは、TypeSystemDefinitionOrExtension を含めることができます。

例えば次のドキュメントは、元の実行スキーマが提供された型拡張を知らない可能性があるため、実行するには無効です:

Counter Example № 106query getDogName {
  dog {
    name
    color
  }
}

extend type Dog {
  color: String
}

5.2操作(Operations)

5.2.1すべての操作定義

5.2.1.1操作型の存在

形式的仕様
  • ドキュメント内の各 operation 定義 operation について:
    • rootOperationType を、operation の種類に対応する schema 内の root operation type とします。
    • rootOperationType は存在しなければなりません。
説明文

スキーマはサポートする各種類の操作に対する root operation type を定義します。すべてのスキーマは query 操作をサポートしなければなりませんが、mutationsubscription のサポートは任意です。

もしドキュメントで定義された操作の種類に対してスキーマが必要な root operation type を含まない場合、その操作は実行できないため無効です。

例えば次のようなスキーマが与えられているとします:

Example № 107type Query {
  hello: String
}

次の操作は有効です:

Example № 108query helloQuery {
  hello
}

一方、次の操作は無効です:

Counter Example № 109mutation goodbyeMutation {
  goodbye
}

5.2.2名前付き操作定義

5.2.2.1操作名の一意性

形式的仕様
  • ドキュメント内の各 operation 定義 operation について:
    • operationNameoperation の名前とします。
    • もし operationName が存在する場合:
      • operations をドキュメント内で名前が operationName のすべての operation 定義とします。
      • operations は 1 個の集合でなければなりません。
説明文

各名前付き operation 定義は、その名前で参照されるときに文書内で一意でなければなりません。

例えば次のドキュメントは有効です:

Example № 110query getDogName {
  dog {
    name
  }
}

query getOwnerName {
  dog {
    owner {
      name
    }
  }
}

一方、次のドキュメントは無効です:

Counter Example № 111query getName {
  dog {
    name
  }
}

query getName {
  dog {
    owner {
      name
    }
  }
}

各操作の種類が異なっていても無効です:

Counter Example № 112query dogOperation {
  dog {
    name
  }
}

mutation dogOperation {
  mutateDog {
    id
  }
}

5.2.3匿名操作定義

5.2.3.1単独の匿名操作

形式的仕様
  • ドキュメント内のすべての operation 定義を operations とします。
  • ドキュメント内のすべての無名の operation 定義を anonymous とします。
  • もし operations が 1 より多い集合であるなら:
    • anonymous は空でなければなりません。
説明文

GraphQL は、ドキュメント内にその単一の操作のみが存在する場合に、省略記法でクエリ操作を定義することを許容します。

例えば次のドキュメントは有効です:

Example № 113{
  dog {
    name
  }
}

一方、次のドキュメントは無効です:

Counter Example № 114{
  dog {
    name
  }
}

query getName {
  dog {
    owner {
      name
    }
  }
}

5.2.4サブスクリプション操作定義

5.2.4.1単一ルートフィールド

形式的仕様
  • Let subscriptionType be the root Subscription type in schema.
  • ドキュメント内の各サブスクリプション操作定義 subscription について:
    • Let selectionSet be the top level selection set on subscription.
    • Let collectedFieldsMap be the result of CollectSubscriptionFields(subscriptionType, selectionSet).
    • collectedFieldsMap must have exactly one entry, which must not be an introspection field.
CollectSubscriptionFields(objectType, selectionSet, visitedFragments)
  1. もし visitedFragments が提供されていなければ、 空の集合で初期化する。
  2. collectedFieldsMap を、空の順序付き集合を値とする空の順序付きマップで初期化する。
  3. selectionSet 内の各 selection について:
    1. selection@skip ディレクティブを指定してはならない。
    2. selection@include ディレクティブを指定してはならない。
    3. もし selectionField である場合:
      1. responseNameselectionresponse name とする(alias が定義されていればそれ、さもなければフィールド名)。
      2. fieldsForResponseKey を、キーが responseName のときの collectedFieldsMap 内の field set の値とする。存在しない場合は空の順序付き集合でエントリを作成する。
      3. selectionfieldsForResponseKey に追加する。
    4. もし selectionFragmentSpread である場合:
      1. fragmentSpreadNameselection の名前とする。
      2. もし fragmentSpreadNamevisitedFragments に含まれているなら、次の selection に進む。
      3. fragmentSpreadNamevisitedFragments に追加する。
      4. fragment を、現在のドキュメント内で名前が fragmentSpreadName である Fragment とする。
      5. もしそのような fragment が存在しなければ、次の selection に進む。
      6. fragmentTypefragment の型条件とする。
      7. もし DoesFragmentTypeApply(objectType, fragmentType)false であれば、次の selection に進む。
      8. fragmentSelectionSetfragment の最上位選択集合とする。
      9. fragmentCollectedFieldsMap を、CollectSubscriptionFields(objectType, fragmentSelectionSet, visitedFragments) の結果とする。
      10. fragmentCollectedFieldsMap 内の各 responseNamefragmentFields について:
        1. fieldsForResponseKey を、キーが responseName のときの collectedFieldsMap 内の field set の値とする。存在しない場合は空の順序付き集合でエントリを作成する。
        2. fragmentFields の各項目を fieldsForResponseKey に追加する。
    5. もし selectionInlineFragment である場合:
      1. fragmentTypeselection の型条件とする。
      2. もし fragmentType が null でなく、かつ DoesFragmentTypeApply(objectType, fragmentType)false であれば、次の selection に進む。
      3. fragmentSelectionSetselection の最上位選択集合とする。
      4. fragmentCollectedFieldsMap を、CollectSubscriptionFields(objectType, fragmentSelectionSet, visitedFragments) の結果とする。
      5. fragmentCollectedFieldsMap 内の各 responseNamefragmentFields について:
        1. fieldsForResponseKey を、キーが responseName のときの collectedFieldsMap 内の field set の値とする。存在しない場合は空の順序付き集合でエントリを作成する。
        2. fragmentFields の各項目を fieldsForResponseKey に追加する。
  4. collectedFieldsMap を返す。
このアルゴリズムは CollectFields() と非常に似ていますが、実行時変数にアクセスできないため @skip および @include のディレクティブは使用できない、という点が異なります。
説明文

サブスクリプション操作は正確に一つのルートフィールドを持たなければなりません。

実行時変数にアクセスせずにこれを判断できるようにするため、ルート選択集合では @skip@include のディレクティブを禁止する必要があります。

有効な例:

Example № 115subscription sub {
  newMessage {
    body
    sender
  }
}
Example № 116subscription sub {
  ...newMessageFields
}

fragment newMessageFields on Subscription {
  newMessage {
    body
    sender
  }
}

無効:

Counter Example № 117subscription sub {
  newMessage {
    body
    sender
  }
  disallowedSecondRootField
}
Counter Example № 118subscription sub {
  ...multipleSubscriptions
}

fragment multipleSubscriptions on Subscription {
  newMessage {
    body
    sender
  }
  disallowedSecondRootField
}

サブスクリプション操作のルートでは @skip@include のディレクティブを許可しません。次の例も無効です:

Counter Example № 119subscription requiredRuntimeValidation($bool: Boolean!) {
  newMessage @include(if: $bool) {
    body
    sender
  }
  disallowedSecondRootField @skip(if: $bool)
}

サブスクリプション操作のルートフィールドはイントロスペクションフィールドであってはなりません。次の例も無効です:

Counter Example № 120subscription sub {
  __typename
}
各サブスクリプションは正確に一つのルートフィールドを持たなければなりませんが、ドキュメントは任意の数の操作を含めることができ、それぞれ異なるルートフィールドを持ち得ます。実行時に複数のサブスクリプション操作を含むドキュメントを実行する場合は、GetOperation() に記載されているように操作名を指定する必要があります。

5.3Fields(フィールド)

5.3.1Field Selections(フィールド選択)

Field selections はオブジェクト型、インターフェース型、およびユニオン型の上に存在していなければなりません。

Formal Specification(形式的仕様)
  • ドキュメント内の各 selection について:
    • fieldName を当該 selection のターゲットフィールドとする。
    • fieldName はそのスコープ内の型上で定義されていなければなりません。
Explanatory Text(説明)

フィールド選択のターゲットフィールドは、その選択集合のスコープ型上で定義されていなければなりません。エイリアス名には制限はありません。

例えば次のフラグメントは検証に通りません:

Counter Example № 121fragment fieldNotDefined on Dog {
  meowVolume
}

fragment aliasedLyingFieldTargetNotDefined on Dog {
  barkVolume: kawVolume
}

インターフェースについては、直接のフィールド選択はそのインターフェース上で定義されたフィールドに対してのみ可能です。具象実装型のフィールドは、与えられたインターフェース型の選択集合の妥当性には関係ありません。

例えば次は有効です:

Example № 122fragment interfaceFieldSelection on Pet {
  name
}

次は無効です:

Counter Example № 123fragment definedOnImplementersButNotInterface on Pet {
  nickname
}

ユニオンはフィールドを定義しないため、ユニオン型の選択集合から直接フィールドを選択することはできません(例外はメタフィールド __typename のみです)。ユニオン型選択集合のフィールドはフラグメントを介して間接的にのみ問い合わせる必要があります。

例えば次は有効です:

Example № 124fragment inDirectFieldSelectionOnUnion on CatOrDog {
  __typename
  ... on Pet {
    name
  }
  ... on Dog {
    barkVolume
  }
}

しかし次は無効です:

Counter Example № 125fragment directFieldSelectionOnUnion on CatOrDog {
  name
  barkVolume
}

5.3.2Field Selection Merging(フィールド選択のマージ)

Formal Specification(形式的仕様)
  • set をドキュメント内の任意の選択集合とします。
  • FieldsInSetCanMerge(set) は true でなければなりません。
FieldsInSetCanMerge(set)
  1. fieldsForName を、fragment と inline fragment を訪問した結果も含む、set 内の与えられた response name を持つ選択の集合とします。
  2. fieldsForName の異なる各組の要素 fieldAfieldB について:
    1. SameResponseShape(fieldA, fieldB) は true でなければなりません。
    2. もし fieldA および fieldB の親型が等しいか、いずれかが Object Type でない場合:
      1. fieldAfieldB は同一のフィールド名でなければなりません。
      2. fieldAfieldB は同一の引数集合を持たなければなりません。
      3. mergedSetfieldA の選択集合と fieldB の選択集合を加えた結果とします。
      4. FieldsInSetCanMerge(mergedSet) は true でなければなりません。
SameResponseShape(fieldA, fieldB)
  1. typeAfieldA の戻り型とします。
  2. typeBfieldB の戻り型とします。
  3. もし typeA または typeB が Non-Null であれば:
    1. もし typeAtypeB のうちいずれかが nullable であれば、false を返す。
    2. typeA をその nullable 型にする。
    3. typeB をその nullable 型にする。
  4. もし typeA または typeB が List であれば:
    1. もし一方が List でなければ false を返す。
    2. typeA をその要素型にする。
    3. typeB をその要素型にする。
    4. ステップ3から繰り返す。
  5. もし typeA または typeB が Scalar または Enum であれば:
    1. もし両者が同一の型であれば true を返し、そうでなければ false を返す。
  6. 断言: typeA はオブジェクト、ユニオンまたはインターフェース型である。
  7. 断言: typeB はオブジェクト、ユニオンまたはインターフェース型である。
  8. mergedSetfieldA の選択集合と fieldB の選択集合を加えた結果とする。
  9. fieldsForName を、fragment と inline fragment を訪問した結果も含む、与えられた response name を持つ mergedSet 内の選択集合とする。
  10. fieldsForName の各組の異なる要素 subfieldAsubfieldB について:
    1. もし SameResponseShape(subfieldA, subfieldB)false であれば false を返す。
  11. true を返す。
Note 以前の仕様では「composite」という用語はオブジェクト、インターフェース、ユニオン型のいずれかを意味するために使用されていました。
Explanatory Text(説明)

同じ response name を持つ複数のフィールド選択が実行時に同時に出現する場合、実行するフィールドと引数および生成される値が曖昧であってはなりません。したがって同じオブジェクトに対して同時に出現し得る任意の二つのフィールド選択は同等である場合にのみ有効です。

実行中、同じ response name を持つフィールドを同時に実行するために、実行前に CollectSubfields() を実行します。

手書きの単純な GraphQL ではこのルールは明白な開発者エラーですが、ネストしたフラグメントにより手動で検出するのが困難になる場合があります。

次の選択は正しくマージされます:

Example № 126fragment mergeIdenticalFields on Dog {
  name
  name
}

fragment mergeIdenticalAliasesAndFields on Dog {
  otherName: name
  otherName: name
}

次はマージできません:

Counter Example № 127fragment conflictingBecauseAlias on Dog {
  name: nickname
  name
}

同一フィールドは同一の引数を持つ場合にもマージされます。値と変数の両方が正しくマージされ得ます。

例えば次は正しくマージされます:

Example № 128fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
  doesKnowCommand(dogCommand: SIT)
  doesKnowCommand(dogCommand: SIT)
}

fragment mergeIdenticalFieldsWithIdenticalValues on Dog {
  doesKnowCommand(dogCommand: $dogCommand)
  doesKnowCommand(dogCommand: $dogCommand)
}

次は正しくマージされません:

Counter Example № 129fragment 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
}

次のフィールドは一緒にマージされませんが、同じオブジェクトに対して同時に出現し得ないため安全です:

Example № 130fragment 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)
  }
}

しかし、フィールドのレスポンスはマージ可能な形でなければなりません。例えばリーフ型は異なっていてはなりません。この例では someValueStringInt のどちらかになり得ます:

Counter Example № 131fragment conflictingDifferingResponses on Pet {
  ... on Dog {
    someValue: nickname
  }
  ... on Cat {
    someValue: meowVolume
  }
}

5.3.3Leaf Field Selections(リーフフィールド選択)

Formal Specification(形式的仕様)
  • ドキュメント内の各 selection について:
    • selectionType を当該 selection のアンラップされた結果型とする。
    • もし selectionType がスカラーまたは列挙型であれば:その選択のサブ選択集合は空でなければなりません。
    • もし selectionType がインターフェース、ユニオン、またはオブジェクトであれば:その選択のサブ選択集合は空であってはなりません。
Explanatory Text(説明)

リーフフィールドに対してフィールドのサブ選択は許されません。リーフフィールドとはアンラップ後の型がスカラーまたは列挙型である任意のフィールドです。

次は有効です。

Example № 132fragment scalarSelection on Dog {
  barkVolume
}

次は無効です。

Counter Example № 133fragment scalarSelectionsNotAllowedOnInt on Dog {
  barkVolume {
    sinceWhen
  }
}

逆に、非リーフフィールドはフィールドのサブ選択を持たねばなりません。非リーフフィールドとはアンラップ後の型がオブジェクト、インターフェース、またはユニオンである任意のフィールドです。

次の追加をクエリのルート型に加えたと仮定します:

Example № 134extend type Query {
  human: Human
  pet: Pet
  catOrDog: CatOrDog
}

次の例は、非リーフフィールドにサブ選択が含まれていないため無効です。

Counter Example № 135query directQueryOnObjectWithoutSubFields {
  human
}

query directQueryOnInterfaceWithoutSubFields {
  pet
}

query directQueryOnUnionWithoutSubFields {
  catOrDog
}

しかし次の例は、フィールドサブ選択を含むため有効です。

Example № 136query directQueryOnObjectWithSubFields {
  human {
    name
  }
}

5.4Arguments(引数)

Arguments はフィールドとディレクティブの両方に提供されます。以下の検証ルールは両ケースに適用されます。

5.4.1Argument Names(引数名)

Formal Specification(形式的仕様)
  • ドキュメント内の各 argument について:
    • argumentNameargument の Name とします。
    • argumentDefinition を、親フィールドまたは定義が argumentName として提供する引数定義とします。
    • argumentDefinition は存在しなければなりません。
Explanatory Text(説明)

フィールドやディレクティブに提供される各引数は、そのフィールドやディレクティブの可能な引数の集合で定義されていなければなりません。

例えば、次のものは有効です:

Example № 137fragment argOnRequiredArg on Dog {
  doesKnowCommand(dogCommand: SIT)
}

fragment argOnOptional on Dog {
  isHouseTrained(atOtherHomes: true) @include(if: true)
}

次は無効です。なぜなら commandDogCommand 上に定義されていないためです。

Counter Example № 138fragment invalidArgName on Dog {
  doesKnowCommand(command: CLEAN_UP_HOUSE)
}

また次も無効です。unless@include 上に定義されていないためです。

Counter Example № 139fragment invalidArgName on Dog {
  isHouseTrained(atOtherHomes: true) @include(unless: false)
}

より複雑な引数例を検討するために、次を型システムに追加します:

Example № 140type 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
}

引数の順序は重要ではありません。したがって次の両方の例は有効です。

Example № 141fragment multipleArgs on Arguments {
  multipleRequirements(x: 1, y: 2)
}

fragment multipleArgsReverseOrder on Arguments {
  multipleRequirements(y: 2, x: 1)
}

5.4.2Argument Uniqueness(引数の一意性)

フィールドとディレクティブは引数を名前から値へのマッピングとして扱います。同一の引数セット内に同じ名前を持つ引数が複数存在することは曖昧であり無効です。

Formal Specification(形式的仕様)
  • ドキュメント内の各 argument について:
    • argumentNameargument の Name とします。
    • arguments を、当該 argument を含む Argument Set 内で argumentName という名前のすべての Arguments とします。
    • arguments は当該 argument だけを含む集合でなければなりません。

5.4.3Required Arguments(必須引数)

  • ドキュメント内の各 Field または Directive について:
    • arguments を、その Field または Directive によって提供された引数とします。
    • argumentDefinitions を、その Field または Directive の引数定義の集合とします。
    • argumentDefinitions 内の各 argumentDefinition について:
      • type を当該 argumentDefinition の期待型とします。
      • defaultValue を当該 argumentDefinition のデフォルト値とします。
      • もし type が Non-Null であり defaultValue が存在しない場合:
        • argumentNameargumentDefinition の名前とします。
        • argumentarguments の中で argumentName という名前の argument とします。
        • argument は存在しなければなりません。
        • valueargument の値とします。
        • value はリテラルの null であってはなりません。
Explanatory Text(説明)

引数は必須になり得ます。引数の型が non-null かつデフォルト値を持たない場合、その引数は必須です。そうでなければ引数はオプションです。

例えば次のものは有効です:

Example № 142fragment goodBooleanArg on Arguments {
  booleanArgField(booleanArg: true)
}

fragment goodNonNullArg on Arguments {
  nonNullBooleanArgField(nonNullBooleanArg: true)
}

nullable な引数を持つフィールドからは引数を省略できます。

したがって次のフラグメントは有効です:

Example № 143fragment goodBooleanArgDefault on Arguments {
  booleanArgField
}

しかしこれは必須引数に対しては無効です。

Counter Example № 144fragment missingRequiredArg on Arguments {
  nonNullBooleanArgField
}

明示的にリテラルの null を与えることも無効です。必須引数は常に non-null 型を持つためです。

Counter Example № 145fragment missingRequiredArg on Arguments {
  nonNullBooleanArgField(nonNullBooleanArg: null)
}

5.5Fragments(フラグメント)

5.5.1Fragment Declarations(フラグメント宣言)

5.5.1.1Fragment Name Uniqueness(フラグメント名の一意性)

Formal Specification(形式的仕様)
  • ドキュメント内の各フラグメント定義 fragment について:
    • fragmentName をその fragment の名前とします。
    • fragments をドキュメント内で名前が fragmentName のすべてのフラグメント定義とします。
    • fragments は 1 個の集合でなければなりません。
Explanatory Text(説明)

フラグメント定義はフラグメントスプレッドで名前により参照されます。曖昧さを避けるため、各フラグメントの名前はドキュメント内で一意でなければなりません。

インラインフラグメントはフラグメント定義とは見なされず、この検証ルールの影響を受けません。

例えば次のドキュメントは有効です:

Example № 146{
  dog {
    ...fragmentOne
    ...fragmentTwo
  }
}

fragment fragmentOne on Dog {
  name
}

fragment fragmentTwo on Dog {
  owner {
    name
  }
}

一方、次のドキュメントは無効です:

Counter Example № 147{
  dog {
    ...fragmentOne
  }
}

fragment fragmentOne on Dog {
  name
}

fragment fragmentOne on Dog {
  owner {
    name
  }
}

5.5.1.2Fragment Spread Type Existence(フラグメントの型の存在)

Formal Specification(形式的仕様)
  • ドキュメント内の各 named spread namedSpread について:
    • fragmentnamedSpread のターゲットとします。
    • fragment のターゲット型はスキーマ内に定義されていなければなりません。
Explanatory Text(説明)

フラグメントはスキーマに存在する型上で指定されなければなりません。これは名前付きフラグメントとインラインフラグメントの両方に適用されます。スキーマに定義されていない場合、そのフラグメントは無効です。

例えば次のフラグメントは有効です:

Example № 148fragment correctType on Dog {
  name
}

fragment inlineFragment on Dog {
  ... on Dog {
    name
  }
}

fragment inlineFragment2 on Dog {
  ... @include(if: true) {
    name
  }
}

次のものは妥当ではありません:

Counter Example № 149fragment notOnExistingType on NotInSchema {
  name
}

fragment inlineNotExistingType on Dog {
  ... on NotInSchema {
    name
  }
}

5.5.1.3Fragments on Object, Interface or Union Types(オブジェクト・インターフェース・ユニオン上のフラグメント)

Formal Specification(形式的仕様)
  • ドキュメント内の各フラグメント fragment について:
    • そのフラグメントのターゲット型は UNIONINTERFACE、または OBJECT のいずれかの kind を持たなければなりません。
Explanatory Text(説明)

フラグメントはユニオン、インターフェース、オブジェクト上でのみ宣言できます。スカラー上では無効です。非リーフフィールド上でのみ適用できます。このルールはインラインと名前付きの両方のフラグメントに適用されます。

次のフラグメント宣言は有効です:

Example № 150fragment fragOnObject on Dog {
  name
}

fragment fragOnInterface on Pet {
  name
}

fragment fragOnUnion on CatOrDog {
  ... on Dog {
    name
  }
}

次のものは無効です:

Counter Example № 151fragment fragOnScalar on Int {
  something
}

fragment inlineFragOnScalar on Dog {
  ... on Boolean {
    somethingElse
  }
}

5.5.1.4Fragments Must Be Used(フラグメントは使用されねばならない)

Formal Specification(形式的仕様)
  • ドキュメント内の各フラグメント fragment について:
    • fragment はドキュメント内の少なくとも 1 回のスプレッドのターゲットでなければなりません。
Explanatory Text(説明)

定義されたフラグメントはドキュメント内で使用されなければなりません。

例えば次は無効なドキュメントです:

Counter Example № 152fragment nameFragment on Dog { # unused
  name
}

{
  dog {
    name
  }
}

5.5.2Fragment Spreads(フラグメントスプレッド)

フィールド選択はフラグメントを互いにスプレッドすることで決定されます。ターゲットフラグメントの選択集合は、ターゲットフラグメントが参照されるレベルの選択集合に結合されます。

5.5.2.1Fragment Spread Target Defined(スプレッド先の定義)

Formal Specification(形式的仕様)
  • ドキュメント内のすべての namedSpread について:
    • fragmentnamedSpread のターゲットとします。
    • fragment はドキュメント内で定義されていなければなりません。
Explanatory Text(説明)

名前付きフラグメントスプレッドはドキュメント内で定義されたフラグメントを参照しなければなりません。スプレッドのターゲットが定義されていない場合、それは検証エラーです。

Counter Example № 153{
  dog {
    ...undefinedFragment
  }
}

5.5.2.2Fragment Spreads Must Not Form Cycles(フラグメントスプレッドはサイクルを形成してはならない)

Formal Specification(形式的仕様)
  • ドキュメント内の各 fragmentDefinition について:
    • visited を空集合とします。
    • DetectFragmentCycles(fragmentDefinition, visited) を実行します。
DetectFragmentCycles(fragmentDefinition, visited)
  1. spreads を当該 fragmentDefinition のすべてのフラグメントスプレッドの子孫とします。
  2. spread について:
    1. visitedspread を含んではなりません。
    2. nextVisitedspreadvisited のメンバーを含む集合とします。
    3. nextFragmentDefinitionspread のターゲットとします。
    4. DetectFragmentCycles(nextFragmentDefinition, nextVisited) を再帰的に呼び出します。
Explanatory Text(説明)

フラグメントスプレッドのグラフは、自己にスプレッドすることを含め、いかなるサイクルも形成してはなりません。さもなければ操作は無限にスプレッドしたり、基底データのサイクルに対して無限に実行されたりする可能性があります。

次は無限スプレッドを招くため無効となるフラグメントです:

Counter Example № 154{
  dog {
    ...nameFragment
  }
}

fragment nameFragment on Dog {
  name
  ...barkVolumeFragment
}

fragment barkVolumeFragment on Dog {
  barkVolume
  ...nameFragment
}

上記のフラグメントをインライン化すると、次のように無限に大きくなります:

Example № 155{
  dog {
    name
    barkVolume
    name
    barkVolume
    name
    barkVolume
    name
    # forever...
  }
}

また、循環するデータに対して実行すると無限再帰を招くフラグメントも無効とします:

Counter Example № 156{
  dog {
    ...dogFragment
  }
}

fragment dogFragment on Dog {
  name
  owner {
    ...ownerFragment
  }
}

fragment ownerFragment on Human {
  name
  pets {
    ...dogFragment
  }
}

5.5.2.3Fragment Spread Is Possible(フラグメントスプレッドが可能であること)

Formal Specification(形式的仕様)
  • ドキュメント内の各 spread(名前付きおよびインライン)について:
    • fragment を当該 spread のターゲットとします。
    • fragmentType を当該 fragment の型条件とします。
    • parentTypespread を含む選択集合の型とします。
    • applicableTypesGetPossibleTypes(fragmentType)GetPossibleTypes(parentType) の共通部分とします。
    • applicableTypes は空であってはなりません。
GetPossibleTypes(type)
  1. もし type がオブジェクト型であれば、その type を含む集合を返します。
  2. もし type がインターフェース型であれば、そのインターフェースを実装する型の集合を返します。
  3. もし type がユニオン型であれば、その可能な型の集合を返します。
Explanatory Text(説明)

フラグメントは型に宣言され、そのランタイムのオブジェクト型が型条件に一致するときにのみ適用されます。フラグメントは親型の文脈内でスプレッドされます。フラグメントスプレッドは、その型条件が親型の下で一度でも適用され得る場合にのみ有効です。

5.5.2.3.1Object Spreads in Object Scope(オブジェクトスコープ内でのオブジェクトスプレッド)

オブジェクト型のスコープでは、そのスコープにあるのと同じ型に適用されるオブジェクト型フラグメントスプレッドのみが有効です。

例えば

Example № 157fragment dogFragment on Dog {
  ... on Dog {
    barkVolume
  }
}

次は無効です:

Counter Example № 158fragment catInDogFragmentInvalid on Dog {
  ... on Cat {
    meowVolume
  }
}
5.5.2.3.2Abstract Spreads in Object Scope(オブジェクトスコープ内での抽象スプレッド)

オブジェクト型のスコープでは、インターフェースやユニオンのスプレッドは、そのオブジェクト型がそのインターフェースを実装するか、ユニオンのメンバーである場合に使用できます。

例えば

Example № 159fragment petNameFragment on Pet {
  name
}

fragment interfaceWithinObjectFragment on Dog {
  ...petNameFragment
}

これは Dog が Pet を実装しているため有効です。

同様に

Example № 160fragment catOrDogNameFragment on CatOrDog {
  ... on Cat {
    meowVolume
  }
}

fragment unionWithObjectFragment on Dog {
  ...catOrDogNameFragment
}

これは DogCatOrDog ユニオンのメンバーであるため有効です。CatOrDogNameFragment の内容を調べると有効な結果が決して返らないことが分かるかもしれませんが、我々は宣言部のみを検討しその本文は検証で無効としないことに注意してください。

5.5.2.3.3Object Spreads in Abstract Scope(抽象スコープ内でのオブジェクトスプレッド)

ユニオンやインターフェースのスコープ内でもオブジェクトのスプレッドは使用できますが、そのオブジェクト型が当該インターフェースやユニオンの可能な型の一つである場合に限ります。

例えば、次のフラグメントは有効です:

Example № 161fragment petFragment on Pet {
  name
  ... on Dog {
    barkVolume
  }
}

fragment catOrDogFragment on CatOrDog {
  ... on Cat {
    meowVolume
  }
}

petFragmentDog がインターフェース Pet を実装しているため有効です。catOrDogFragmentCatCatOrDog ユニオンのメンバーであるため有効です。

対照的に次のフラグメントは無効です:

Counter Example № 162fragment sentientFragment on Sentient {
  ... on Dog {
    barkVolume
  }
}

fragment humanOrAlienFragment on HumanOrAlien {
  ... on Cat {
    meowVolume
  }
}

Dog はインターフェース Sentient を実装していないため、sentientFragment は有意な結果を返せません。したがってそのフラグメントは無効です。同様に Cat はユニオン HumanOrAlien のメンバーではないため humanOrAlienFragment も無効です。

5.5.2.3.4Abstract Spreads in Abstract Scope(抽象スコープ内の抽象スプレッド)

インターフェースやユニオンのフラグメントは互いに使用できます。スコープとスプレッドの可能な型の交差に少なくとも 1 つのオブジェクト型が存在する限り、そのスプレッドは有効と見なされます。

例えば

Example № 163fragment unionWithInterface on Pet {
  ...dogOrHumanFragment
}

fragment dogOrHumanFragment on DogOrHuman {
  ... on Dog {
    barkVolume
  }
}

これは Dog がインターフェース Pet を実装し、かつ DogOrHuman のメンバーであるため有効と見なされます。

しかし

Counter Example № 164fragment nonIntersectingInterfaces on Pet {
  ...sentientFragment
}

fragment sentientFragment on Sentient {
  name
}

これは PetSentient の両方を実装する型が存在しないため無効です。

Interface Spreads in Implemented Interface Scope(実装されたインターフェーススコープ内のインターフェーススプレッド)

さらに、あるインターフェース型のフラグメントは、そのインターフェースを実装する別のインターフェースのスコープ内に常にスプレッドできます。

下の例では、...resourceFragment のスプレッドは有効です。なぜなら ResourceNode を実装しているからです。

Example № 165interface 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 について:
    • 当該 value が見つかる位置で期待される型を type とする。
    • 当該 valuetype に強制(coerce)可能でなければなりません(value 内にネストされた任意の variableUsage が、その位置で使用可能なランタイム値を表すという前提の下で)。
説明文

リテラル値は、型システム章で定義されたコーシング規則に従って、その値が現れる位置で期待される型と互換でなければなりません。

ListValueObjectValue はネストされた入力値を含むことがあり、それらの一部は変数使用(variable usage)である可能性があります。すべての変数使用が許可される 検証ルールは、各 variableUsage がその位置で使用可能な型であることを保証します。変数値の強制変換 アルゴリズムは、変数のランタイム値が正しくコースされることを保証します。したがって、この検証ルールにおける「コース可能(coercible)」という主張の目的のために、各 variableUsage のランタイム値はその位置での使用に対して有効であると仮定できます。

位置で期待される型には、値が提供される引数によって定義された型、値が提供される入力オブジェクトフィールドによって定義された型、およびデフォルト値が提供される変数定義の型が含まれます。

以下の例は値リテラルの有効な使用例です:

Example № 166fragment 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: FindDogInput = { name: "Fido" }) {
  findDog(searchBy: $search) {
    name
  }
}

mutation addPet($pet: PetInput! = { cat: { name: "Brontie" } }) {
  addPet(pet: $pet) {
    name
  }
}

(文字列を整数に等価変換するなどの)コース不可能な値は無効です。次の例は無効です:

Counter Example № 167fragment stringIntoInt on Arguments {
  intArgField(intArg: "123")
}

query badComplexValue {
  findDog(searchBy: { name: 123 }) {
    name
  }
}

mutation oneOfWithNoFields {
  addPet(pet: {}) {
    name
  }
}

mutation oneOfWithTwoFields($dog: DogInput) {
  addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) {
    name
  }
}

mutation listOfOneOfWithNullableVariable($dog: DogInput) {
  addPets(pets: [{ dog: $dog }]) {
    name
  }
}

5.6.2入力オブジェクトのフィールド名

形式的仕様
  • ドキュメント内の各入力オブジェクトフィールド inputField について:
    • inputFieldName を当該 inputField の名前とする。
    • inputFieldDefinition を、親の入力オブジェクト型が inputFieldName として提供する入力フィールド定義とする。
    • inputFieldDefinition は存在しなければならない。
説明文

入力オブジェクト値内に指定されたすべての入力フィールドは、その入力オブジェクトの期待される型の可能なフィールド集合で定義されていなければなりません。

例えば次の入力オブジェクトの例は有効です:

Example № 168{
  findDog(searchBy: { name: "Fido" }) {
    name
  }
}

一方、次の入力オブジェクトの例は、期待される型に定義されていないフィールド "favoriteCookieFlavor" を使用しているため無効です:

Counter Example № 169{
  findDog(searchBy: { favoriteCookieFlavor: "Bacon" }) {
    name
  }
}

5.6.3入力オブジェクトフィールドの一意性

形式的仕様
  • ドキュメント内の各入力オブジェクト値 inputObject について:
    • 当該 inputObject 内の各入力フィールド inputField について:
      • name を当該 inputField の名前とする。
      • fields を当該 inputObject 内で名前が name のすべての入力オブジェクトフィールドとする。
      • fields は当該 inputField のみを含む集合でなければならない。
説明文

入力オブジェクトは同じ名前のフィールドを複数含んではなりません。そうでなければ曖昧さが生じ、構文の一部が無視されることになります。

例えば次のドキュメントは検証に通りません。

Counter Example № 170{
  field(arg: { field: true, field: false })
}

5.6.4入力オブジェクトの必須フィールド

形式的仕様
  • ドキュメント内の各入力オブジェクトについて:
    • 当該入力オブジェクトによって提供されるフィールドを fields とする。
    • 当該入力オブジェクトの入力フィールド定義の集合を fieldDefinitions とする。
    • 当該 fieldDefinitions 内の各 fieldDefinition について:
      • 当該 fieldDefinition の期待型を type とする。
      • 当該 fieldDefinition のデフォルト値を defaultValue とする。
      • もし type が Non-Null であり defaultValue が存在しない場合:
        • 当該 fieldDefinition の名前を fieldName とする。
        • 当該 fields の中で名前が fieldName の入力フィールドを field とする。
        • field は存在しなければならない。
        • 当該 field の値を value とする。
        • value はリテラルの null であってはならない。
説明文

入力オブジェクトのフィールドは必須になり得ます。フィールドが必須かどうかは、そのフィールドの型が non-null でデフォルト値を持たないかどうかによって決まります。そうでなければその入力フィールドはオプションです。

5.7ディレクティブ

5.7.1ディレクティブが定義されていること

形式的仕様
  • ドキュメント内の各 directive について:
    • 当該 directive の名前を directiveName とする。
    • directiveName という名前のディレクティブ定義を directiveDefinition とする。
    • directiveDefinition は存在しなければならない。
説明文

GraphQL サービスはサポートするディレクティブを定義します。ディレクティブを利用する各箇所に対して、そのサービスでそのディレクティブが利用可能でなければなりません。

5.7.2ディレクティブが有効な場所にあること

形式的仕様
  • ドキュメント内の各 directive について:
    • 当該 directive の名前を directiveName とする。
    • 当該 directiveName という名前のディレクティブ定義を directiveDefinition とする。
    • 当該 directiveDefinition の有効な位置の集合を locations とする。
    • 当該ディレクティブが影響を与える AST ノードを adjacent とする。
    • adjacentlocations 内の項目で表されなければならない。
説明文

GraphQL サービスは、どのディレクティブをどこでサポートするかを定義します。ディレクティブの各使用箇所は、そのサービスがサポートを宣言した場所で使用されなければなりません。

例えば次のドキュメントは検証に通りません。なぜなら @skipQUERY を有効な位置として提供していないからです。

Counter Example № 171query @skip(if: $foo) {
  field
}

5.7.3ディレクティブは位置ごとに一意であること

形式的仕様
  • ドキュメント内でディレクティブが適用され得る各 location について:
    • 当該 location に適用され、かつ repeatable でないディレクティブの集合を directives とする。
    • 当該 directives の各ディレクティブ directive について:
      • 当該 directive の名前を directiveName とする。
      • 当該 directives の中で名前が directiveName のすべてのディレクティブを namedDirectives とする。
      • namedDirectives は 1 個のみを含む集合でなければならない。
説明文

GraphQL は、repeatable として定義されたディレクティブが、異なる引数で同一の定義箇所に複数回使用されることを許可します。対照的に、ディレクティブが repeatable でない場合は、その位置につきそのディレクティブは一度だけ使用可能です。

例えば次のドキュメントは検証に通りません。なぜなら非 repeatable な @skip が同じフィールドに対して二度使われているからです:

Counter Example № 172query ($foo: Boolean = true, $bar: Boolean = false) {
  field @skip(if: $foo) @skip(if: $bar)
}

しかし次の例は有効です。なぜなら同一の位置につき @skip が一度だけ使用されているためで、操作全体や同じ名前のフィールド上では複数回使用されている場合があっても問題ありません:

Example № 173query ($foo: Boolean = true, $bar: Boolean = false) {
  field @skip(if: $foo) {
    subfieldA
  }
  field @skip(if: $bar) {
    subfieldB
  }
}

5.8Variables(変数)

5.8.1Variable Uniqueness(変数の一意性)

Formal Specification(形式的仕様)
  • For every operation in the document:
    • For every variable defined on operation:
      • Let variableName be the name of variable.
      • Let variables be the set of all variables named variableName on operation.
      • variables must be a set of one.
Explanatory Text(説明文)

もし同じ名前の変数を同一の操作が二つ以上定義している場合、それは曖昧で無効です。重複した変数の型が同じであっても無効となります。

Counter Example № 174query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

複数の操作が同じ名前の変数を定義することは有効です。もし二つの操作が同じフラグメントを参照するなら、それが必要な場合もあります:

Example № 175query A($atOtherHomes: Boolean) {
  ...HouseTrainedFragment
}

query B($atOtherHomes: Boolean) {
  ...HouseTrainedFragment
}

fragment HouseTrainedFragment on Query {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

5.8.2Variables Are Input Types(変数は入力型であること)

Formal Specification(形式的仕様)
  • For every operation in a document:
    • For every variable on each operation:
      • Let variableType be the type of variable.
      • IsInputType(variableType) must be true.
Explanatory Text(説明文)

変数は入力型でなければなりません。Object、Union、Interface は入力として使えません。

以下の例では、次の型システムの追加を考慮してください:

Example № 176extend type Query {
  booleanList(booleanListArg: [Boolean!]): Boolean
}

次の操作は有効です:

Example № 177query takesBoolean($atOtherHomes: Boolean) {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

query takesComplexInput($search: FindDogInput) {
  findDog(searchBy: $search) {
    name
  }
}

query TakesListOfBooleanBang($booleans: [Boolean!]) {
  booleanList(booleanListArg: $booleans)
}

次の操作は無効です:

Counter Example № 178query takesCat($cat: Cat) {
  # ...
}

query takesDogBang($dog: Dog!) {
  # ...
}

query takesListOfPet($pets: [Pet]) {
  # ...
}

query takesCatOrDog($catOrDog: CatOrDog) {
  # ...
}

5.8.3All Variable Uses Defined(すべての変数使用が定義されていること)

Formal Specification(形式的仕様)
  • For each operation in a document:
    • For each variableUsage in scope, variable must be in operation‘s variable list.
    • Let fragments be every fragment referenced by that operation transitively.
    • For each fragment in fragments:
      • For each variableUsage in scope of fragment, variable must be in operation‘s variable list.
Explanatory Text(説明文)

変数は操作ごとにスコープされます。つまり、操作の文脈内で使用される任意の変数は、その操作のトップレベルで定義されていなければなりません。

例えば:

Example № 179query variableIsDefined($atOtherHomes: Boolean) {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

は有効です。$atOtherHomes は操作で定義されています。

対照的に次のドキュメントは無効です:

Counter Example № 180query variableIsNotDefined {
  dog {
    isHouseTrained(atOtherHomes: $atOtherHomes)
  }
}

$atOtherHomes はその操作で定義されていません。

フラグメントはこのルールを複雑にします。操作が遷移的に含む任意のフラグメントは、その操作で定義された変数にアクセスできます。フラグメントは複数の操作に現れる可能性があるため、変数使用はそれらすべての操作の変数定義に対応していなければなりません。

例えば次は有効です:

Example № 181query variableIsDefinedUsedInSingleFragment($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

上の例は、isHouseTrainedFragment が操作 variableIsDefinedUsedInSingleFragment の文脈内で使用され、その操作が変数を定義しているため有効です。

一方、もしフラグメントが参照された操作が該当する変数を定義していなければ、そのドキュメントは無効です。

Counter Example № 182query variableIsNotDefinedUsedInSingleFragment {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

これは遷移的にも適用されます。したがって次も失敗します:

Counter Example № 183query variableIsNotDefinedUsedInNestedFragment {
  dog {
    ...outerHouseTrainedFragment
  }
}

fragment outerHouseTrainedFragment on Dog {
  ...isHouseTrainedFragment
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

フラグメントが使用されるすべての操作で変数が定義されていなければなりません。

Example № 184query houseTrainedQueryOne($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

query houseTrainedQueryTwo($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

しかし次の例は妥当ではありません:

Counter Example № 185query houseTrainedQueryOne($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

query houseTrainedQueryTwoNotDefined {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

これは、houseTrainedQueryTwoNotDefined が $atOtherHomes を定義しておらず、その変数が含まれているフラグメント isHouseTrainedFragment がその操作内で参照されているためです。

5.8.4All Variables Used(すべての変数が使用されていること)

Formal Specification(形式的仕様)
  • For every operation in the document:
    • Let variables be the variables defined by that operation.
    • Each variable in variables must be used at least once in either the operation scope itself or any fragment transitively referenced by that operation.
Explanatory Text(説明文)

操作で定義されたすべての変数は、その操作自身またはその操作が遷移的に含むフラグメントのいずれかで使用されなければなりません。未使用の変数は検証エラーを引き起こします。

例えば次は無効です:

Counter Example № 186query variableUnused($atOtherHomes: Boolean) {
  dog {
    isHouseTrained
  }
}

これは $atOtherHomes が参照されていないためです。

これらのルールは遷移的なフラグメントスプレッドにも適用されます:

Example № 187query variableUsedInFragment($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

上の例は有効です。なぜなら $atOtherHomes が、variableUsedInFragment に含まれる isHouseTrainedFragment で使用されているからです。

もしそのフラグメントが $atOtherHomes を参照していなければ無効になります:

Counter Example № 188query variableNotUsedWithinFragment($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedWithoutVariableFragment
  }
}

fragment isHouseTrainedWithoutVariableFragment on Dog {
  isHouseTrained
}

ドキュメント中のすべての操作は、その操作で定義されたすべての変数を使用しなければなりません。

その結果、次のドキュメントは検証されません:

Counter Example № 189query queryWithUsedVar($atOtherHomes: Boolean) {
  dog {
    ...isHouseTrainedFragment
  }
}

query queryWithExtraVar($atOtherHomes: Boolean, $extra: Int) {
  dog {
    ...isHouseTrainedFragment
  }
}

fragment isHouseTrainedFragment on Dog {
  isHouseTrained(atOtherHomes: $atOtherHomes)
}

このドキュメントは、queryWithExtraVar が余分な変数を定義しているため無効です。

5.8.5All Variable Usages Are Allowed(すべての変数使用が許容されること)

Formal Specification(形式的仕様)
  • For each operation in document:
    • Let variableUsages be all usages transitively included in the operation.
    • For each variableUsage in variableUsages:
      • Let variableName be the name of variableUsage.
      • Let variableDefinition be the VariableDefinition named variableName defined within operation.
      • IsVariableUsageAllowed(variableDefinition, variableUsage) must be true.
IsVariableUsageAllowed(variableDefinition, variableUsage)
  1. Let variableType be the expected type of variableDefinition.
  2. Let locationType be the expected type of the Argument, ObjectField, or ListValue entry where variableUsage is located.
  3. If IsNonNullPosition(locationType, variableUsage) AND variableType is NOT a non-null type:
    1. Let hasNonNullVariableDefaultValue be true if a default value exists for variableDefinition and is not the value null.
    2. Let hasLocationDefaultValue be true if a default value exists for the Argument or ObjectField where variableUsage is located.
    3. If hasNonNullVariableDefaultValue is NOT true AND hasLocationDefaultValue is NOT true, return false.
    4. Let nullableLocationType be the unwrapped nullable type of locationType.
    5. Return AreTypesCompatible(variableType, nullableLocationType).
  4. Return AreTypesCompatible(variableType, locationType).
IsNonNullPosition(locationType, variableUsage)
  1. If locationType is a non-null type, return true.
  2. If the location of variableUsage is an ObjectField:
    1. Let parentObjectValue be the ObjectValue containing ObjectField.
    2. Let parentLocationType be the expected type of ObjectValue.
    3. If parentLocationType is a OneOf Input Object type, return true.
  3. Return false.
AreTypesCompatible(variableType, locationType)
  1. If locationType is a non-null type:
    1. If variableType is NOT a non-null type, return false.
    2. Let nullableLocationType be the unwrapped nullable type of locationType.
    3. Let nullableVariableType be the unwrapped nullable type of variableType.
    4. Return AreTypesCompatible(nullableVariableType, nullableLocationType).
  2. Otherwise, if variableType is a non-null type:
    1. Let nullableVariableType be the nullable type of variableType.
    2. Return AreTypesCompatible(nullableVariableType, locationType).
  3. Otherwise, if locationType is a list type:
    1. If variableType is NOT a list type, return false.
    2. Let itemLocationType be the unwrapped item type of locationType.
    3. Let itemVariableType be the unwrapped item type of variableType.
    4. Return AreTypesCompatible(itemVariableType, itemLocationType).
  4. Otherwise, if variableType is a list type, return false.
  5. Return true if variableType and locationType are identical, otherwise false.
Explanatory Text(説明文)

変数の使用は、それが渡される引数と互換でなければなりません。

型がまったく合致しない場合や、nullable な変数が non-null 引数に渡される場合に検証失敗が発生します。

型は一致しなければなりません:

Counter Example № 190query intCannotGoIntoBoolean($intArg: Int) {
  arguments {
    booleanArgField(booleanArg: $intArg)
  }
}

$intArgInt 型なので、Boolean 型の booleanArg に使うことはできません。

リストの次数(cardinality)も同様に一致している必要があります。例えばリストを単一値に渡すことはできません。

Counter Example № 191query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) {
  arguments {
    booleanArgField(booleanArg: $booleanListArg)
  }
}

nullable 性も尊重される必要があります。一般に nullable な変数は non-null な引数に渡せません。

Counter Example № 192query booleanArgQuery($booleanArg: Boolean) {
  arguments {
    nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
  }
}

リスト型については、外側と内側の両方の nullable のルールが適用されます。nullable なリストは non-null なリストに渡せませんし、nullable な値を含むリストは non-null な値のリストに渡せません。以下は有効です:

Example № 193query nonNullListToList($nonNullBooleanList: [Boolean]!) {
  arguments {
    booleanListArgField(booleanListArg: $nonNullBooleanList)
  }
}

しかし nullable なリストは non-null なリストに渡せません:

Counter Example № 194query listToNonNullList($booleanList: [Boolean]) {
  arguments {
    nonNullBooleanListField(nonNullBooleanListArg: $booleanList)
  }
}

これは [T][T]! に渡せないため検証に失敗します。同様に [T][T!] に渡すこともできません。

OneOf Input Object フィールドに用いる変数は非 nullable でなければなりません。

Example № 195mutation addCat($cat: CatInput!) {
  addPet(pet: { cat: $cat }) {
    name
  }
}

mutation addCatWithDefault($cat: CatInput! = { name: "Brontie" }) {
  addPet(pet: { cat: $cat }) {
    name
  }
}
Counter Example № 196mutation addNullableCat($cat: CatInput) {
  addPet(pet: { cat: $cat }) {
    name
  }
}
Allowing Optional Variables When Default Values Exist(デフォルト値が存在する場合にオプション変数を許容すること)

典型的な変数型互換性の注目すべき例外は、変数定義が nullable 型であっても、その変数またはその場所(引数)がデフォルト値を提供している場合には non-null な場所に渡すことを許容する点です。

下の例では、optional な変数 $booleanArg が、スキーマ側でデフォルト値を提供するためにフィールド引数が optional であり、非 null 引数 optionalBooleanArg に使えることが示されています。

Example № 197query booleanArgQueryWithDefault($booleanArg: Boolean) {
  arguments {
    optionalNonNullBooleanArgField(optionalBooleanArg: $booleanArg)
  }
}

次の例では、optional な変数 $booleanArg が操作側でデフォルト値を提供しているため、non-null な引数(nonNullBooleanArg)に使えることが許容されています。この挙動は旧仕様との互換性のために明示的にサポートされています。GraphQL 作成ツールはこれを警告として報告し、曖昧さを避けるために BooleanBoolean! に変更することを提案しても良いでしょう。

Example № 198query booleanArgQueryWithDefault($booleanArg: Boolean = true) {
  arguments {
    nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
  }
}
Note ランタイムではそのような変数に対して null が提供される可能性が依然としてあります。もし非 null 引数に null が提供された場合、実行時エラーを発生させなければなりません(execution error)。

6Execution(実行)

GraphQL サービスはリクエストから実行を通してレスポンスを生成します。

実行のための request はいくつかの情報から構成されます:

これらの情報が与えられたとき、ExecuteRequest(schema, document, operationName, variableValues, initialValue) の結果としてレスポンスが生成されます。レスポンスの形式は下の Response セクションに従います。

実装者は将来の GraphQL 仕様版と衝突する可能性があるため、request に対して追加プロパティを付与すべきではありません。代わりに extensions は実装固有の追加情報用の予約領域を提供します。存在する場合、extensions はマップでなければなりませんが、その内容に対する追加の制約はありません。衝突を避けるため、キーはユニークなプレフィックスを使うべきです。

Note GraphQL リクエストは特定のシリアライズ形式やトランスポート機構を要求しません。メッセージのシリアライズやトランスポートの選択は実装するサービスによって決められるべきです。
Note 実行可能ドキュメント(operation definitions、fragment definitions、variable definitions)内の説明やコメントは、実行時には無視されなければならず、GraphQL ドキュメントの観測可能な実行、検証、またはレスポンスに影響を与えてはなりません。実行可能ドキュメント上の説明やコメントはログやその他の開発者ツールなど観測不能な目的で使用しても構いません。

6.1Executing Requests(リクエストの実行)

リクエストを実行するために、実行者は解析済みの Document を持っている必要があります。ドキュメントが複数の操作を定義する場合は実行する操作名を選択する必要があります。そうでなければドキュメントは単一の操作のみを含むことが期待されます。リクエストの結果は下記「Executing Operations(操作の実行)」セクションに従ってその操作を実行した結果によって決まります。

ExecuteRequest(schema, document, operationName, variableValues, initialValue)
  1. Let operation be the result of GetOperation(document, operationName).
  2. Let coercedVariableValues be the result of CoerceVariableValues(schema, operation, variableValues).
  3. If operation is a query operation:
    1. Return ExecuteQuery(operation, schema, coercedVariableValues, initialValue).
  4. Otherwise if operation is a mutation operation:
    1. Return ExecuteMutation(operation, schema, coercedVariableValues, initialValue).
  5. Otherwise if operation is a subscription operation:
    1. Return Subscribe(operation, schema, coercedVariableValues, initialValue).
GetOperation(document, operationName)
  1. If operationName is null:
    1. If document contains exactly one operation.
      1. Return the Operation contained in the document.
    2. Otherwise raise a request error requiring operationName.
  2. Otherwise:
    1. Let operation be the Operation named operationName in document.
    2. If operation was not found, raise a request error.
    3. Return operation.

6.1.1Validating Requests(リクエストの検証)

Validation セクションで説明されているように、すべての検証ルールを通過したリクエストのみを実行すべきです。もし検証エラーが既に判明している場合、それらはレスポンスの "errors" リストに報告され、リクエストは実行せずに失敗しなければなりません。

通常、検証は実行直前のリクエストの文脈で行われますが、実装は同一のリクエストが以前に検証済みであることが既に分かっている場合、直ちに検証せずに実行することもできます。GraphQL サービスは「ある時点で」検証エラーがないことが知られており、それ以降変更されていないリクエストのみ実行するべきです。

例えば:リクエストは開発中に検証され、その後変更されていない限り実行してもよい、またはサービスは一度リクエストを検証して結果をメモ化し、同じリクエストを将来再度検証するのを避けることができます。

6.1.2Coercing Variable Values(変数値の強制変換)

操作が変数を定義している場合、これらの変数に与えられた値は変数の宣言された型の入力強制規則を用いて強制(coerce)される必要があります。変数値の入力強制中に request error が発生した場合、その操作は実行せずに失敗します。

CoerceVariableValues(schema, operation, variableValues)
  1. Let coercedValues be an empty unordered Map.
  2. Let variablesDefinition be the variables defined by operation.
  3. For each variableDefinition in variablesDefinition:
    1. Let variableName be the name of variableDefinition.
    2. Let variableType be the expected type of variableDefinition.
    3. Assert: IsInputType(variableType) must be true.
    4. Let defaultValue be the default value for variableDefinition.
    5. Let hasValue be true if variableValues provides a value for the name variableName.
    6. Let value be the value provided in variableValues for the name variableName.
    7. If hasValue is not true and defaultValue exists (including null):
      1. Let coercedDefaultValue be the result of coercing defaultValue according to the input coercion rules of variableType.
      2. Add an entry to coercedValues named variableName with the value coercedDefaultValue.
    8. Otherwise if variableType is a Non-Nullable type, and either hasValue is not true or value is null, raise a request error.
    9. Otherwise if hasValue is true:
      1. If value is null:
        1. Add an entry to coercedValues named variableName with the value null.
      2. Otherwise:
        1. If value cannot be coerced according to the input coercion rules of variableType, raise a request error.
        2. Let coercedValue be the result of coercing value according to the input coercion rules of variableType.
        3. Add an entry to coercedValues named variableName with the value coercedValue.
  4. Return coercedValues.
Note このアルゴリズムは CoerceArgumentValues() と非常に似ています。

6.2Executing Operations(操作の実行)

「Type System」セクションで述べたとおり、型システムはクエリのルート操作型を提供しなければなりません。もし mutation や subscription をサポートする場合、それぞれ mutation または subscription のルート操作型も提供する必要があります。

6.2.1Query(クエリ)

操作がクエリである場合、操作の結果はその操作の root selection set をクエリのルート操作型で実行した結果です。

クエリ操作を実行する際に initial value を提供しても構いません。

ExecuteQuery(query, schema, variableValues, initialValue)
  1. Let queryType be the root Query type in schema.
  2. Assert: queryType is an Object type.
  3. Let rootSelectionSet be the root selection set in query.
  4. Return ExecuteRootSelectionSet(variableValues, initialValue, queryType, rootSelectionSet, "normal").

6.2.2Mutation(ミューテーション)

操作がミューテーションである場合、操作の結果は mutation ルートオブジェクト型上でその操作の root selection set を実行した結果です。この選択集合は直列に実行されるべきです。

ミューテーション操作のトップレベルフィールドは基盤データシステムに対して副作用を行うことが期待されます。これら副作用に関する競合状態を避けるため、指定されたミューテーションは直列実行されます。

ExecuteMutation(mutation, schema, variableValues, initialValue)
  1. Let mutationType be the root Mutation type in schema.
  2. Assert: mutationType is an Object type.
  3. Let rootSelectionSet be the root selection set in mutation.
  4. Return ExecuteRootSelectionSet(variableValues, initialValue, mutationType, rootSelectionSet, "serial").

6.2.3Subscription(サブスクリプション)

操作がサブスクリプションである場合、結果は event stream(応答ストリーム)であり、イベントストリーム内の各イベントは基盤の source stream 上の新しいイベントごとにその操作を実行した結果です。

サブスクリプション操作を実行すると、サービス上に永続的な関数が作成され、基盤の source stream を返される response stream にマッピングします。

Subscribe(subscription, schema, variableValues, initialValue)
  1. Let sourceStream be the result of running CreateSourceEventStream(subscription, schema, variableValues, initialValue).
  2. Let responseStream be the result of running MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues).
  3. Return responseStream.
Note 大規模なサブスクリプションシステムでは、Subscribe()ExecuteSubscriptionEvent() のアルゴリズムを別々のサービスで実行し、予測可能なスケーリング特性を維持する場合があります。Supporting Subscriptions at Scale の節を参照してください。

例としてチャットアプリケーションを考えます。チャットルームの新しいメッセージを購読するには、クライアントは次のようなリクエストを送信します:

Example № 199subscription NewMessages {
  newMessage(roomId: 123) {
    sender
    text
  }
}

クライアントが購読している間、ID が "123" のチャットルームに新しいメッセージが投稿されるたびに "sender" と "text" の選択が評価されクライアントに公開されます。例えば:

Example № 200{
  "data": {
    "newMessage": {
      "sender": "Hagrid",
      "text": "You're a wizard!"
    }
  }
}

「チャットルームに新しいメッセージが投稿された」ことは、チャットルームID を「トピック」とし、各「publish」が送信者とテキストを含む「Pub-Sub」システムを使うことで実現できます。

Event Streams

event stream はイベントの列(時間に沿って観測可能な離散的な発行値の系列)を表します。例えば「トピックへの購読」はトピックに対する各「publish」に対して値が発行される event stream を生成するかもしれません。

event stream は任意の時点で完了することがあります(多くの場合それ以上のイベントが発生しないため)。event stream は無限列を発行し続けることもあり、その場合完了しないかもしれません。もし event stream がエラーに遭遇した場合、それはそのエラーで完了しなければなりません。

オブザーバは任意の時点で event stream の観測を停止(キャンセル)することができます。event stream がキャンセルされると、それは完了しなければなりません。

内部のユーザコードも任意の理由で event stream をキャンセルでき、その場合は当該 event stream が完了することとして観測されます。

Supporting Subscriptions at Scale

クエリとミューテーション操作はステートレスであり、GraphQL サービスインスタンスを複製することでスケールさせることができます。対照的にサブスクリプションはステートフルであり、サブスクリプションのライフタイムを通じて GraphQL ドキュメント、変数、その他のコンテキストを維持する必要があります。

単一マシンの障害によって状態が失われる場合のシステムの動作を検討してください。サブスクリプション状態とクライアント接続性を管理する専用サービスを用意することで、耐久性と可用性を改善できる場合があります。

Delivery Agnostic

GraphQL サブスクリプションは特定のシリアライズ形式やトランスポート機構を要求しません。GraphQL はレスポンスストリームの作成、ストリーム上の各ペイロードの内容、およびストリームの終了に関するアルゴリズムを規定しますが、メッセージ確認、バッファリング、再送要求、その他 QoS の詳細については故意に規定していません。メッセージのシリアライズ、トランスポート機構、および QoS の詳細は実装サービスが選択するべきです。

6.2.3.1Source Stream(ソースストリーム)

source stream はルート値の系列を表す event stream であり、それぞれのルート値が GraphQL の実行をトリガーします。フィールド値の解決と同様に、source stream を作成するロジックはアプリケーション固有です。

CreateSourceEventStream(subscription, schema, variableValues, initialValue)
  1. Let subscriptionType be the root Subscription type in schema.
  2. Assert: subscriptionType is an Object type.
  3. Let selectionSet be the top level selection set in subscription.
  4. Let collectedFieldsMap be the result of CollectFields(subscriptionType, selectionSet, variableValues).
  5. If collectedFieldsMap does not have exactly one entry, raise a request error.
  6. Let fields be the value of the first entry in collectedFieldsMap.
  7. Let fieldName be the name of the first entry in fields. Note: This value is unaffected if an alias is used.
  8. Let field be the first entry in fields.
  9. Let argumentValues be the result of CoerceArgumentValues(subscriptionType, field, variableValues).
  10. Let sourceStream be the result of running ResolveFieldEventStream(subscriptionType, initialValue, fieldName, argumentValues).
  11. Return sourceStream.
ResolveFieldEventStream(subscriptionType, rootValue, fieldName, argumentValues)
  1. Let resolver be the internal function provided by subscriptionType for determining the resolved event stream of a subscription field named fieldName.
  2. Return the result of calling resolver, providing rootValue and argumentValues.
Note この ResolveFieldEventStream() アルゴリズムは、任意の操作型上でのリゾルバ定義に一貫性を持たせるために ResolveFieldValue() と意図的に類似しています。

6.2.3.2Response Stream(応答ストリーム)

基盤の source stream からの各イベントは、そのイベントの値を initialValue としてサブスクリプションの selection set を実行するトリガーになります。

MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues)
  1. Let responseStream be a new event stream.
  2. When sourceStream emits sourceValue:
    1. Let executionResult be the result of running ExecuteSubscriptionEvent(subscription, schema, variableValues, sourceValue).
    2. If internal error was raised:
      1. Cancel sourceStream.
      2. Complete responseStream with error.
    3. Otherwise emit executionResult on responseStream.
  3. When sourceStream completes normally:
    1. Complete responseStream normally.
  4. When sourceStream completes with error:
    1. Complete responseStream with error.
  5. When responseStream is cancelled:
    1. Cancel sourceStream.
    2. Complete responseStream normally.
  6. Return responseStream.
Note ExecuteSubscriptionEvent() はすべての execution error を扱うため、request errorCreateSourceEventStream() の間にしか発生しません。したがって ExecuteSubscriptionEvent() から扱われる残りのエラー条件は、この仕様で説明されていない内部的な例外的エラーだけです。
ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue)
  1. Let subscriptionType be the root Subscription type in schema.
  2. Assert: subscriptionType is an Object type.
  3. Let rootSelectionSet be the root selection set in subscription.
  4. Return ExecuteRootSelectionSet(variableValues, initialValue, subscriptionType, rootSelectionSet, "normal").
Note ExecuteSubscriptionEvent() のアルゴリズムは、各イベント結果がどのように生成されるかという点で ExecuteQuery() と意図的に類似しています。

6.2.3.3解除

Unsubscribe はクライアントがもはやサブスクリプションのペイロードを受け取りたくないときに、response stream をキャンセルします。これにより Source Stream もキャンセルされ、サブスクリプションで使用していた他のリソースをクリーンアップする良い機会となります。

Unsubscribe(responseStream)
  1. responseStream をキャンセルします。

6.3選択セットの実行

GraphQL 操作を実行する際には、操作内で選択されたすべてのフィールドを再帰的に収集して実行します。まず操作の最上位の root selection set から最初に選択されたすべてのフィールドを収集し、それぞれを実行します。各フィールドが完了すると、そのすべてのサブフィールドが収集され、それらを順次実行します。このプロセスは、もはや収集・実行するサブフィールドがなくなるまで続きます。

6.3.1ルート選択セットの実行

root selection set は GraphQL 操作が提供する最上位の selection set です。root selection set は常に root operation type から選択します。

ルート選択集合を実行するには、評価される initial value とルート型、そしてフィールドを直列に実行するか通常どおり(並列で全フィールドを実行する)かを把握している必要があります(Normal and Serial Execution を参照)。

クエリ(並列)、ミューテーション(直列)、およびサブスクリプション(基盤の Source Stream の各イベントごとに実行される)に対して、ルート選択集合の実行は同様に動作します。

まず、selection set を収集して collected fields map にし、それを実行して結果の dataerrors を返します。

ExecuteRootSelectionSet(variableValues, initialValue, objectType, selectionSet, executionMode)
  1. Let collectedFieldsMap be the result of CollectFields(objectType, selectionSet, variableValues).
  2. Let data be the result of running ExecuteCollectedFields(collectedFieldsMap, objectType, initialValue, variableValues) serially if executionMode is "serial", otherwise normally (allowing parallelization)).
  3. Let errors be the list of all execution error raised while executing the selection set.
  4. Return an unordered map containing data and errors.

6.3.2フィールド収集

実行前に、各 selection set は、同じレスポンス名を持つすべてのフィールド(参照されるフラグメント内のものも含む)を個別の field set にまとめることで、collected fields map に変換されます。これにより同じレスポンス名への複数の参照があっても、そのフィールドは一度だけ実行されます。

collected fields map は順序付きマップで、各エントリは response name とそれに関連する field set です。collected fields mapCollectFields() によって selection set から作られるか、または CollectSubfields() によって field set のすべてのエントリの selection set から作られます。

field set は同じ response name を共有する選択フィールドの順序付き集合です(エイリアスが定義されていればエイリアス、それ以外はフィールド名が使用されます)。検証によりセット内の各フィールドは同じ名前と引数を持つことが保証されますが、それぞれ異なるサブフィールドを持つ可能性があります(詳細は: Field Selection Merging を参照)。

Note collected fields map および field set の両方におけるフィールド選択の順序は重要であるため、この仕様のアルゴリズムはそれらを順序付きマップおよび順序付き集合としてモデル化しています。

例えば、このクエリの選択集合を収集すると、二つのエントリ、"a""b" を持つ collected fields map が得られ、a は二つのインスタンス、b は一つのインスタンスになります:

Example № 201{
  a {
    subfield1
  }
  ...ExampleFragment
}

fragment ExampleFragment on Query {
  a {
    subfield2
  }
  b
}

CollectFields() によって生成された各 field set の深さ優先探索順序は実行を通じて保たれ、フィールドが実行されたレスポンス内で安定した予測可能な順序で表示されることを保証します。

CollectFields(objectType, selectionSet, variableValues, visitedFragments)
  1. If visitedFragments is not provided, initialize it to the empty set.
  2. Initialize collectedFieldsMap to an empty ordered map of ordered sets.
  3. For each selection in selectionSet:
    1. If selection provides the directive @skip, let skipDirective be that directive.
      1. If skipDirective‘s if argument is true or is a variable in variableValues with the value true, continue with the next selection in selectionSet.
    2. If selection provides the directive @include, let includeDirective be that directive.
      1. If includeDirective‘s if argument is not true and is not a variable in variableValues with the value true, continue with the next selection in selectionSet.
    3. If selection is a Field:
      1. Let responseName be the response name of selection (the alias if defined, otherwise the field name).
      2. Let fieldsForResponseName be the field set value in collectedFieldsMap for the key responseName; otherwise create the entry with an empty ordered set.
      3. Add selection to the fieldsForResponseName.
    4. If selection is a FragmentSpread:
      1. Let fragmentSpreadName be the name of selection.
      2. If fragmentSpreadName is in visitedFragments, continue with the next selection in selectionSet.
      3. Add fragmentSpreadName to visitedFragments.
      4. Let fragment be the Fragment in the current Document whose name is fragmentSpreadName.
      5. If no such fragment exists, continue with the next selection in selectionSet.
      6. Let fragmentType be the type condition on fragment.
      7. If DoesFragmentTypeApply(objectType, fragmentType) is false, continue with the next selection in selectionSet.
      8. Let fragmentSelectionSet be the top-level selection set of fragment.
      9. Let fragmentCollectedFieldsMap be the result of calling CollectFields(objectType, fragmentSelectionSet, variableValues, visitedFragments).
      10. For each responseName and fragmentFields in fragmentCollectedFieldsMap:
        1. Let fieldsForResponseName be the field set value in collectedFieldsMap for the key responseName; otherwise create the entry with an empty ordered set.
        2. Add each item from fragmentFields to fieldsForResponseName.
    5. If selection is an InlineFragment:
      1. Let fragmentType be the type condition on selection.
      2. If fragmentType is not null and DoesFragmentTypeApply(objectType, fragmentType) is false, continue with the next selection in selectionSet.
      3. Let fragmentSelectionSet be the top-level selection set of selection.
      4. Let fragmentCollectedFieldsMap be the result of calling CollectFields(objectType, fragmentSelectionSet, variableValues, visitedFragments).
      5. For each responseName and fragmentFields in fragmentCollectedFieldsMap:
        1. Let fieldsForResponseName be the field set value in collectedFieldsMap for the key responseName; otherwise create the entry with an empty ordered set.
        2. Append each item from fragmentFields to fieldsForResponseName.
  4. Return collectedFieldsMap.
DoesFragmentTypeApply(objectType, fragmentType)
  1. If fragmentType is an Object Type:
    1. If objectType and fragmentType are the same type, return true, otherwise return false.
  2. If fragmentType is an Interface Type:
    1. If objectType is an implementation of fragmentType, return true, otherwise return false.
  3. If fragmentType is a Union:
    1. If objectType is a possible type of fragmentType, return true, otherwise return false.
Note CollectFields() における @skip@include ディレクティブの評価手順は、それらが可換に適用されるため、どちらの順序で適用してもよいです。
Merging Selection Sets

オブジェクト型フィールドのサブ選択を実行するために、親の field set 内で同じレスポンス名を持つ各フィールドのすべての selection sets をマージして、次に実行するサブフィールドを表す単一の collected fields map にします。

同名のサブ選択を持つ並列フィールドを示す例操作です。

前の例を続けると、

Example № 202{
  a {
    subfield1
  }
  ...ExampleFragment
}

fragment ExampleFragment on Query {
  a {
    subfield2
  }
  b
}

フィールド "a" の値を解決した後、複数の selection set が収集されマージされ、"subfield1""subfield2" が同じフェーズで同じ値に対して解決されます。

CollectSubfields(objectType, fields, variableValues)
  1. Let collectedFieldsMap be an empty ordered map of ordered sets.
  2. For each field in fields:
    1. Let fieldSelectionSet be the selection set of field.
    2. If fieldSelectionSet is null or empty, continue to the next field.
    3. Let fieldCollectedFieldsMap be the result of CollectFields(objectType, fieldSelectionSet, variableValues).
    4. For each responseName and subfields in fieldCollectedFieldsMap:
      1. Let fieldsForResponseName be the field set value in collectedFieldsMap for the key responseName; otherwise create the entry with an empty ordered set.
      2. Add each fields from subfields to fieldsForResponseName.
  3. Return collectedFieldsMap.
Note CollectSubfields() に渡されるすべての fields は同じ response name を共有しています。

6.3.3収集されたフィールドの実行

collected fields map を実行するには、評価されるオブジェクト型とランタイム値、ならびに任意の変数のランタイム値が必要です。

実行は collected fields map の各エントリの値を再帰的に解決および完了し、同じ response name キーで結果マップ内にエントリを生成します。

ExecuteCollectedFields(collectedFieldsMap, objectType, objectValue, variableValues)
  1. Initialize resultMap to an empty ordered map.
  2. For each responseName and fields in collectedFieldsMap:
    1. Let fieldName be the name of the first entry in fields. Note: This value is unaffected if an alias is used.
    2. Let fieldType be the return type defined for the field fieldName of objectType.
    3. If fieldType is defined:
      1. Let responseValue be ExecuteField(objectType, objectValue, fieldType, fields, variableValues).
      2. Set responseValue as the value for responseName in resultMap.
  3. Return resultMap.
Note resultMap は操作内でフィールドが現れる順序に従って順序付けられます。これは Field Collection セクションで詳述されています。
Errors and Non-Null Types

もし ExecuteCollectedFields() の実行中に、非 null 型の response positionexecution error を発生させた場合、そのエラーは親のレスポンス位置に伝播されなければなりません(フィールドの場合は選択集合全体、リスト位置の場合はリスト全体)。伝播先が許すなら null に解決されるか、さらに上位のレスポンス位置へ伝播されます。

このようなことが発生した場合、まだ実行されていないか値をまだ生成していない兄弟レスポンス位置は、不必要な作業を避けるためにキャンセルされることがあります。

Note この振る舞いの詳細は Handling Execution Errors を参照してください。

6.3.4通常実行と直列実行

通常、実行者は collected fields map 内のエントリを任意の順序で(通常は並列に)実行できます。トップレベルのミューテーションフィールド以外のフィールド解決は副作用がなく冪等でなければならないため、実行順序が結果に影響を与えないようになっています。したがってサービスは最適と判断する順序でフィールドエントリを実行する自由があります。

例えば、次のような collected fields map が通常どおり実行される場合:

Example № 203{
  birthday {
    month
  }
  address {
    street
  }
}

有効な GraphQL 実行器は 4 つのフィールドを任意の順序で解決できます(ただしもちろん birthdaymonth の前に解決され、addressstreet の前に解決されなければなりません)。

ミューテーションを実行する際は、最上位の選択集合内の選択はテキスト上で最初に現れるフィールドから順に直列で実行されます。

collected fields map を直列に実行する場合、実行者は collected fields map に提供された順序で各エントリを考慮し、各項目に対応する結果マップ内のエントリを完了するまで次のエントリに進んではなりません。

例えば次のミューテーション操作では、ルートの selection set は直列に実行されなければなりません:

Example № 204mutation ChangeBirthdayAndAddress($newBirthday: String!, $newAddress: String!) {
  changeBirthday(birthday: $newBirthday) {
    month
  }
  changeAddress(address: $newAddress) {
    street
  }
}

したがって実行者は、直列に次のことを行わなければなりません:

  • ExecuteField()changeBirthday に対して実行し、そこで CompleteValue() の間に { month } のサブ選択集合が通常どおり実行されます。
  • ExecuteField()changeAddress に対して実行し、そこで CompleteValue() の間に { street } のサブ選択集合が通常どおり実行されます。

例として、changeTheNumber というミューテーションフィールドがあり、戻り値は theNumber という単一フィールドを含むオブジェクトを返すとします。次の選択集合を直列に実行すると:

Example № 205# Note: This is a selection set, not a full document using the query shorthand.
{
  first: changeTheNumber(newNumber: 1) {
    theNumber
  }
  second: changeTheNumber(newNumber: 3) {
    theNumber
  }
  third: changeTheNumber(newNumber: 2) {
    theNumber
  }
}

実行者は直列に次を実行します:

  • changeTheNumber(newNumber: 1) フィールドを解決する。
  • first{ theNumber } サブ選択集合を通常どおり実行する。
  • changeTheNumber(newNumber: 3) フィールドを解決する。
  • second{ theNumber } サブ選択集合を通常どおり実行する。
  • changeTheNumber(newNumber: 2) フィールドを解決する。
  • third{ theNumber } サブ選択集合を通常どおり実行する。

正しい実行器はその選択集合に対して次の結果を生成しなければなりません:

Example № 206{
  "first": {
    "theNumber": 1
  },
  "second": {
    "theNumber": 3
  },
  "third": {
    "theNumber": 2
  }
}

6.4フィールドの実行

結果マップの各エントリは、collected fields map においてそのフィールド名で選択されたオブジェクト型上のフィールドを実行した結果です。フィールドの実行はまず提供された引数値を強制(coerce)し、次にフィールドの値を解決し、最後にその値を別の選択集合を再帰的に実行するかスカラー値を強制することで完了します。

ExecuteField(objectType, objectValue, fieldType, fields, variableValues)
  1. Let field be the first entry in fields.
  2. Let fieldName be the field name of field.
  3. Let argumentValues be the result of CoerceArgumentValues(objectType, field, variableValues).
  4. Let resolvedValue be ResolveFieldValue(objectType, objectValue, fieldName, argumentValues).
  5. Return the result of CompleteValue(fieldType, fields, resolvedValue, variableValues).

6.4.1フィールド引数の強制型変換

フィールドは、正しく値を生成するために基盤ランタイムへ渡される引数を含むことがあります。これらの引数は型システム内で特定の入力型を持つように定義されています。

操作の各引数位置にはリテラルの Value、または実行時に提供される Variable があり得ます。

CoerceArgumentValues(objectType, field, variableValues)
  1. Let coercedValues be an empty unordered Map.
  2. Let argumentValues be the argument values provided in field.
  3. Let fieldName be the name of field.
  4. Let argumentDefinitions be the arguments defined by objectType for the field named fieldName.
  5. For each argumentDefinition in argumentDefinitions:
    1. Let argumentName be the name of argumentDefinition.
    2. Let argumentType be the expected type of argumentDefinition.
    3. Let defaultValue be the default value for argumentDefinition.
    4. Let argumentValue be the value provided in argumentValues for the name argumentName.
    5. If argumentValue is a Variable:
      1. Let variableName be the name of argumentValue.
      2. If variableValues provides a value for the name variableName:
        1. Let hasValue be true.
        2. Let value be the value provided in variableValues for the name variableName.
    6. Otherwise if argumentValues provides a value for the name argumentName.
      1. Let hasValue be true.
      2. Let value be argumentValue.
    7. If hasValue is not true and defaultValue exists (including null):
      1. Let coercedDefaultValue be the result of coercing defaultValue according to the input coercion rules of argumentType.
      2. Add an entry to coercedValues named argumentName with the value coercedDefaultValue.
    8. Otherwise if argumentType is a Non-Nullable type, and either hasValue is not true or value is null, raise an execution error.
    9. Otherwise if hasValue is true:
      1. If value is null:
        1. Add an entry to coercedValues named argumentName with the value null.
      2. Otherwise, if argumentValue is a Variable:
        1. Add an entry to coercedValues named argumentName with the value value.
      3. Otherwise:
        1. If value cannot be coerced according to the input coercion rules of argumentType, raise an execution error.
        2. Let coercedValue be the result of coercing value according to the input coercion rules of argumentType.
        3. Add an entry to coercedValues named argumentName with the value coercedValue.
  6. Return coercedValues.

request errorCoerceArgumentValues() 中の入力強制の結果として発生した場合、それは代わりに execution error として扱われるべきです。

Note 変数値は CoerceVariableValues() で操作を実行する前に強制されることが期待されるため、ここでは強制されません。かつ有効な操作は適切な型の変数の使用のみを許可します。
Note 実装は引数のデフォルト値の強制を一度だけ行って、その結果をキャッシュすることで最適化することが推奨されます。

6.4.2値の解決

GraphQL の実行は多くの部分を一般化して記述できますが、最終的には GraphQL インターフェースを公開する内部システムが値を提供しなければなりません。これは、与えられた型上の特定のフィールドに対して値を生成する ResolveFieldValue を通じて公開されます。

例えば、objectTypePersonfield"soulMate"、および objectValue に John Lennon を表す値を与えると、Yoko Ono を表す値を返すことが期待されます。

ResolveFieldValue(objectType, objectValue, fieldName, argumentValues)
  1. Let resolver be the internal function provided by objectType for determining the resolved value of a field named fieldName.
  2. Return the result of calling resolver, providing objectValue and argumentValues.
Note resolver が基盤のデータベースやネットワークサービスの読み取りに依存するため非同期であることは一般的です。これにより GraphQL 実行器の残りの部分は非同期実行フローを扱う必要があります。フィールドがリスト型である場合、resolver が返した値のコレクション内の各値自体が非同期に取得され得ます。

6.4.3値の完了

フィールドの値を解決した後、その値が期待される返却型に準拠していることを確認して完了させます。返却型が別のオブジェクト型であれば、フィールド実行プロセスはサブフィールドを収集して実行することにより再帰的に続行します。

CompleteValue(fieldType, fields, result, variableValues)
  1. If the fieldType is a Non-Null type:
    1. Let innerType be the inner type of fieldType.
    2. Let completedResult be the result of calling CompleteValue(innerType, fields, result, variableValues).
    3. If completedResult is null, raise an execution error.
    4. Return completedResult.
  2. If result is null (or another internal value similar to null such as undefined), return null.
  3. If fieldType is a List type:
    1. If result is not a collection of values, raise an execution error.
    2. Let innerType be the inner type of fieldType.
    3. Return a list where each list item is the result of calling CompleteValue(innerType, fields, resultItem, variableValues), where resultItem is each item in result.
  4. If fieldType is a Scalar or Enum type:
    1. Return the result of CoerceResult(fieldType, result).
  5. If fieldType is an Object, Interface, or Union type:
    1. If fieldType is an Object type.
      1. Let objectType be fieldType.
    2. Otherwise if fieldType is an Interface or Union type.
      1. Let objectType be ResolveAbstractType(fieldType, result).
    3. Let collectedFieldsMap be the result of calling CollectSubfields(objectType, fields, variableValues).
    4. Return the result of evaluating ExecuteCollectedFields(collectedFieldsMap, objectType, result, variableValues) normally (allowing for parallelization).
Coercing Results

値完了の主目的は、フィールドリゾルバが返す値が GraphQL 型システムとサービスのスキーマに従って有効であることを保証することです。この「動的型チェック」により、任意のサービスの内部ランタイム上で返却型に関する一貫した保証が提供されます。

組み込みスカラーが結果値をどのように強制するかの詳細については、Scalars の Result Coercion and Serialization 小節を参照してください。

CoerceResult(leafType, value)
  1. Assert: value is not null.
  2. Return the result of calling the internal method provided by the type system for determining the “result coercion” of leafType given the value value. This internal method must return a valid value for the type and not null. Otherwise raise an execution error.
Note フィールドリゾルバが null を返した場合、それは CompleteValue() の中で処理され、CoerceResult() が呼ばれる前に扱われます。したがって CoerceResult() の入力と出力は null であってはなりません。
Resolving Abstract Types

抽象的な返却型(Interface または Union)のフィールドを完了する際には、まずその抽象型を関連する Object 型に解決する必要があります。この決定は内部システムが適切な手段で行います。

Note Java や C# のようなオブジェクト指向環境では、objectValue に対する Object 型を決定する一般的な方法として、objectValue のクラス名を使うことがあります。
ResolveAbstractType(abstractType, objectValue)
  1. Return the result of calling the internal method provided by the type system for determining the Object type of abstractType given the value objectValue.

6.4.4実行エラーの処理

execution error とは、フィールド実行、値の解決、または強制中に特定の response position で発生するエラーです。これらのエラーはレスポンス内で報告されなければなりませんが、部分的な "data" を生成することで「処理」されます。

Note これはデータが存在しない request error とは区別されます(request error はデータなしのリクエストエラー結果をもたらします)。

もしフィールドの解決中に execution error が発生した場合(直接であれリスト内にネストされた場合であれ)、そのエラーが発生した response positionnull に解決されたかのように扱われ、そのエラーは "errors" リストに execution result の一部として追加されなければなりません。

ある レスポンス位置 の解決結果が nullResolveFieldValue() の結果による場合や、実行エラーが発生した場合を含む)であり、その位置が Non-Null 型の場合、その位置に実行エラーが発生する。エラーは "errors" リストに 実行結果 として追加されなければならない。

もし既に "errors" リストに追加された execution error のために response positionnull に解決された場合、"errors" リストをさらに変更してはなりません。つまり、各 response position に対して errors リストには一つのエラーのみを追加すべきです。

Non-Null なレスポンス位置は null になり得ないため、実行エラーは親の response position に伝播されます。もし親のレスポンス位置が null を許容するならそれは null に解決され、そうでなければ親が Non-Null 型であればさらに上位に伝播されます。

もし List 型が Non-Null をラップしており、そのリスト要素のいずれかの response positionnull に解決された場合、そのリスト全体の response positionnull に解決されます。さらにその ListNon-Null にラップされている場合、実行エラーは上位へと継続して伝播します。

リクエストのルートから実行エラーの発生源までのすべての response positionNon-Null 型である場合、execution result"data" エントリは null にすべきです。

7レスポンス

GraphQLサービスがrequestを受け取ると、正しく構成されたレスポンスを返す必要があります。サービスのレスポンスは、要求された操作の実行が成功した場合はその結果を記述し、リクエスト中に発生したエラーについても記述します。

レスポンスは部分的なレスポンスと、もしexecution errorが発生してnullに置き換えられた場合のエラー一覧の両方を含む場合があります。

7.1レスポンスフォーマット

GraphQLリクエストはresponseを返します。responseは、execution resultresponse stream、またはrequest error resultのいずれかです。

7.1.1実行結果

GraphQLリクエストは、操作がクエリやミューテーションで、リクエストに実行が含まれている場合、execution resultを返します。また、サブスクリプションの各イベントごとにsource streamresponse streamexecution resultを発行します。

execution resultはマップでなければなりません。

execution resultは、キー"data"のエントリを含まなければなりません。このエントリの値は「データ」セクションで説明されています。

もし実行中にエラーが発生した場合、execution resultはキー"errors"のエントリを含まなければなりません。このエントリの値は、実行中に発生したexecution errorの空でないリストでなければなりません。各エラーは下記「エラー」セクションに説明されるようなマップでなければなりません。もしリクエストがエラーを発生せずに完了した場合、このエントリは存在してはなりません。

注意 "errors"execution resultに存在する場合、シリアライズ時に最初に表示することでエラーの存在が明確になり役立つ場合があります。

execution resultは、extensionsキーを持つエントリを含んでもよいです。このエントリの値については「拡張」セクションで説明されています。

7.1.2レスポンスストリーム

GraphQLリクエストがサブスクリプション操作の場合、response streamを返します。レスポンスストリームはexecution resultのストリームでなければなりません。

7.1.3リクエストエラー結果

GraphQLリクエストは、1つまたは複数のrequest errorが発生し、実行前にリクエストが失敗した場合、request error resultを返します。このリクエストではレスポンスデータは返されません。

注意 request errorは、情報不足・構文エラー・検証失敗・強制型変換失敗、その他実装側がリクエストの実行前に停止すべきと判断した理由で、実行前に発生する場合があります。

request error resultはマップでなければなりません。

request error resultマップにはキー"errors"のエントリが含まれていなければなりません。この値は、request errorの空でないリストで、request中に発生したものです。返却できるデータがない理由を示すrequest errorが最低1つ含まれていなければなりません。各エラーは下記「エラー」セクションに記載の通りマップでなければなりません。

注意 "errors"キーは、エラーの存在を明確にするためシリアライズ時に最初に現れると便利です。

request error resultマップは、キー"data"のエントリを含んではなりません。

request error resultマップにはextensionsキーのエントリが含まれてもよいです。この値は「拡張」セクションで説明されています。

7.1.4レスポンスポジション

response positionは、実行中に生成されるレスポンスデータ内で一意に識別可能な位置です。これはresultMapの直接のエントリ、または(入れ子になった)リスト値内のポジションです。各レスポンスポジションはresponse pathによって一意に識別されます。

response pathは、レスポンスのルートからそのレスポンスポジションまでのパスセグメント(レスポンス名またはリストインデックス)のリストによりresponse positionを一意に識別します。

response pathの値はパスセグメントのリストでなければなりません。フィールドresponse nameを表すセグメントは文字列で、リストインデックスを表すセグメントは0始まりの整数でなければなりません。エイリアスされたフィールドの場合はエイリアス名を使う必要があります。

response pathがエラー結果に含まれる場合、それはエラーが発生したresponse positionを識別します。

1つのフィールドの実行が複数のレスポンスポジションとなる場合があります。例えば、

例 № 207{
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

heroの名前は、response positionで、response path ["hero", "name"]で示されます。heroのfriendsリストは["hero", "friends"]、1つ目のfriendは["hero", "friends", 0]、そのfriendの名前は["hero", "friends", 0, "name"]となります。

7.1.5データ

"data"エントリは、要求された操作の実行結果です。操作がクエリならクエリのルート型、ミューテーションならミューテーションのルート型のオブジェクトです。

レスポンスデータは、実行中に全レスポンスポジションで解決された結果を集積したものです。

もし実行前にエラーが発生した場合、responserequest error resultで、レスポンスデータはありません。

実行中に有効なレスポンスが作成できないエラーが発生した場合、レスポンスの"data"nullになるべきです。

7.1.6エラー

"errors"エントリは、execution resultrequest error result内で、リクエスト中に発生したエラーの空でないリストです。各エラーは下記のエラー結果フォーマットで記述されるデータのマップです。

リクエストエラー

request errorは、リクエスト処理中に発生し、レスポンスデータが返されないエラーです。通常は実行前に発生し、Document内の文法や検証エラー、実行すべき操作の決定不可、変数の無効な入力値などによって生じます。

リクエストエラーは通常、リクエストクライアントの過失です。

リクエストエラーが発生した場合、responserequest error resultでなければなりません。このマップには"data"エントリがあってはならず、"errors"エントリにエラーが含まれていること、実行は中断されることが必要です。

実行エラー

execution errorは、特定フィールドの実行中に発生し、部分的なレスポンスデータになるエラーです。フィールドの引数の型変換失敗、値解決の内部エラー、結果値の型変換失敗などが原因で発生します。

注意 旧バージョンではexecution errorfield errorと呼ばれていました。

実行エラーは通常、GraphQLサービスの過失です。

execution errorは特定のresponse positionで発生し、任意のレスポンスポジションで発生する可能性があります。実行エラーのレスポンスポジションはエラー応答の"path"エントリで示されます。

あるレスポンスポジションで実行エラーが発生した場合、そのレスポンスポジションはresponse"data"エントリに(null以外では)存在してはならず、"errors"エントリにエラーが含まれていなければなりません。入れ子の実行は停止され、兄弟の実行のみ継続され、部分結果が生成されます(実行エラーの処理参照)。

エラー結果フォーマット

すべてのエラーはキー"message"とともに、開発者向けにエラーを理解・修正するための説明文字列を含めなければなりません。

エラーが要求GraphQLドキュメント内の特定の位置に関連する場合は、キー"locations"とともにロケーションリスト(各要素は"line""column"のキーを持つマップ、両方とも1から始まる正の数)を含めるべきです。

エラーがGraphQLの結果内の特定フィールドに関連する場合は、キー"path"としてresponse pathが含まれていなければなりません。これはエラーが発生したresponse positionを示します。これによりクライアントは、nullが真の値かエラーによるものか識別できます。

例えば、以下の操作でfriendsの名前取得に失敗した場合:

例 № 208{
  hero(episode: $episode) {
    name
    heroFriends: friends {
      id
      name
    }
  }
}

レスポンス例:

例 № 209{
  "errors": [
    {
      "message": "ID 1002 のキャラクターの名前を取得できませんでした。",
      "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の結果は次のnull可能なフィールドまで伝播します。その場合エラーのpathはエラーが発生した結果フィールドへの完全なパスを含める必要があります。

上記でnameフィールドがNon-Null型で宣言されていた場合、結果は異なりますがエラー報告は同じです:

例 № 210{
  "errors": [
    {
      "message": "ID 1002 のキャラクターの名前を取得できませんでした。",
      "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サービスは、エラーにextensionsキーを追加のエントリとして提供しても構いません。このエントリの値はマップでなければなりません。利用者向けにエラーへ独自情報を追加するために使えますが、その内容に制限はありません。

例 № 211{
  "errors": [
    {
      "message": "ID 1002 のキャラクターの名前を取得できませんでした。",
      "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エントリは未記載でした。未指定のエントリは違反ではありませんが推奨されません。
カウンター例 № 212{
  "errors": [
    {
      "message": "ID 1002 のキャラクターの名前を取得できませんでした。",
      "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.1.7拡張

"extensions"エントリは、execution resultrequest error result内で、値はマップでなければなりません。実装者が独自にプロトコルを拡張するためのもので、内容制約はありません。

7.1.8追加エントリ

将来的なプロトコルの変更で既存サービスやクライアントが壊れないよう、execution resultrequest error resultマップは上述したもの以外のエントリを含んではなりません。クライアントは、これら以外のエントリは無視すべきです。

7.2シリアライズ形式

GraphQLは特定のシリアライズ形式を要求しません。しかし、クライアントはGraphQLレスポンスの主要プリミティブをサポートするシリアライズ形式を使うべきです。特に、以下4つのプリミティブの表現を必ずサポートしなければなりません:

  • マップ
  • リスト
  • 文字列
  • Null

また、シリアライズ形式は以下のプリミティブもサポートすべきです(いずれも一般的なGraphQLスカラー型)。ただし、直接サポートされていない場合は文字列またはより単純なプリミティブでも代替可能です:

  • ブール値
  • Int(整数)
  • Float(浮動小数点数)
  • Enum値

これはシリアライズ形式がエンコードできるものの網羅的リストではありません。例えば、日付・時刻・URIや異なる精度の数値などのカスタムスカラーも、シリアライズ形式がサポートする関連形式で表現できます。

7.2.1JSONシリアライズ

JSONはGraphQLで最も一般的なシリアライズ形式です。ただし、前述の通り、GraphQLは特定のシリアライズ形式を要求しません。

GraphQLレスポンスのシリアライズにJSONを使う場合、以下のJSON値によって該当するGraphQL値をエンコードします:

GraphQL値 JSON値
マップ オブジェクト
リスト 配列
Null null
文字列 文字列
ブール値 true または false
Int(整数) 数値
Float(小数) 数値
Enum値 文字列
注意 一貫性・記法の容易さのために、本文中のレスポンス例はすべてJSON形式で示されています。

7.2.2シリアライズ済みマップの順序

selection setを評価した結果は順序があるため、レスポンス結果のマップも、selection set実行で要求されたフィールド順にマップエントリを書き出すべきです。フィールドがリクエストで現れた順でレスポンスを表現することにより、人間によるデバッグの可読性が向上し、プロパティの順序が予測できる場合にレスポンスの解析効率も向上します。

順序を持つマップを表現するシリアライズ形式は、要求されたフィールド順を保持すべきです。順序を持たないがテキスト上の順序がある形式(JSONなど)は、リクエスト順にフィールドを並べるべきです。

例えば、リクエストが{ name, age }なら、JSONで返すときは{ "name": "Mark", "age": 30 }と返すべきで{ "age": 30, "name": "Mark" }のように返すべきではありません。

JSONオブジェクトは定義上順序を持たないキー・値の集合ですが、実際には順序が保持されて表現されています。つまり、JSON文字列{ "name": "Mark", "age": 30 }{ "age": 30, "name": "Mark" }は同じ値をエンコードしますが、プロパティの順番が異なります。

注意 これはJSON仕様違反ではありません。クライアントはレスポンス内オブジェクトを順序なしマップとして解釈し、妥当な値を取得できます。

A付録:適合性

GraphQLの適合実装は、すべての規範的要件を満たす必要があります。適合性要件は、この文書において叙述的断言と明確な意味を持つキーワードの両方で説明されています。

規範部分のキーワード「MUST」「MUST NOT」「REQUIRED」「SHALL」「SHALL NOT」「SHOULD」「SHOULD NOT」「RECOMMENDED」「MAY」「OPTIONAL」は、IETF RFC 2119に記載された通りに解釈されます。これらのキーワードは小文字で現れても、その意味を保持します(非規範と明示されていない限り)。

GraphQLの適合実装は、追加機能を提供しても構いませんが、明示的に禁止されている場合や、非適合となる場合はそれを行ってはなりません。

適合アルゴリズム

命令形で書かれたアルゴリズム手順(例:「リゾルバを呼び出した結果を返す」など)は、そのアルゴリズム本体と同じレベルの必須度で解釈されます。アルゴリズム手順内で参照される他のアルゴリズム(例:「CompleteValue()を呼び出した結果をcompletedResultとする」など)は、包含するアルゴリズムと少なくとも同等レベルの必須度で解釈されます。

アルゴリズムやデータコレクションで表現される適合性要件は、知覚される結果が同等である限り、実装方法は問いません。この文書で記載されるアルゴリズムは分かりやすさを重視しています。実装者は同等だが最適化された実装を含めることが推奨されます。

アルゴリズム、データコレクション、および本書で使用されるその他の記法についての詳細は付録Aを参照してください。

非規範的部分

この文書の内容は、明示的に非規範と宣言された部分を除きすべて規範的です。

この文書中の例は非規範であり、導入された概念や仕様の規範的部分の挙動の理解を助けるために示されています。例は、明示的に本文内で言及される(例:「例えば」)か、次のような例や反例のブロックで区切られて示されます:

例 № 213これは非規範的な例です。
反例 № 214これは非規範的な反例です。

この文書中のノートも非規範的であり、意図の明確化・注意すべき端例や落とし穴への警告・実装時によく生じる疑問への回答として記載されています。ノートは本文で明示的に導入される(例:「注意:」)か、次のようなノートブロックで区切られて示されます:

注意 これは非規範ノートの例です。

B付録:記法の慣例

この仕様書では、言語文法・意味論や実行時アルゴリズムなどの技術的概念を記述するためのさまざまな記法が用いられています。

この付録では、それらの記法について詳しく説明し、曖昧さを避けることを目的としています。

B.1非文脈自由文法

非文脈自由文法は、複数の生成規則(プロダクション)から成ります。各生成規則は「非終端記号」を左辺に持ち、非終端記号や終端文字の並びの0個以上の定義を右辺に持ちます。

1つのゴール非終端記号から開始し、非文脈自由文法は言語を記述します:これは、ゴール列内の任意の非終端記号をその定義のいずれかの並びで再帰的に置き換え続け、すべての非終端記号が終端文字に置き換えられた結果として得られる文字列集合です。

終端記号はこの文書中、等幅フォント(monospace)で二種類の形で表現されます:特定のUnicode文字やUnicode文字列(例:=terminal)、またはプローズ(説明文)で特定コードポイント("Space (U+0020)"など)を記述する形です。Unicode文字列は構文の文法でのみ現れ、指定の並びのNameトークンを表します。

非終端記号の生成規則は、単一定義の場合、次の記法で表されます:

NonTerminalWithSingleDefinition
NonTerminalterminal

複数定義の場合、次の記法で表します:

NonTerminalWithManyDefinitions
OtherNonTerminalterminal
terminal

定義は自己参照することもあり、繰り返しの並び表現となります。例えば:

B.2字句・構文文法

GraphQL言語は、終端記号がトークンとなる構文文法によって定義されます。トークンは、字句文法で定義され、ソース文字のパターンにマッチします。文字列(Unicode文字の並び)のパース結果は、まず字句文法に従って字句トークンの並びを産み、そのトークン列から構文文法に従って抽象構文木(AST)を生成します。

字句文法の生成規則は、終端Unicode文字のパターンとして「トークン(非終端)」を記述します。字句文法内の終端Unicode文字間には「空白」や無視される文字は現れてはなりません。字句文法の定義は二重コロン::で区別されます。

Word
Letterlist

構文文法の生成規則は終端トークンのパターンとして「ルール(非終端)」を記述します。WhitespaceIgnoredのような並びが、いずれの終端Tokenの前後にも現れます。構文文法の定義は一重コロン:で区別されます。

Sentence
Wordlist.

B.3文法記法

この仕様では、よくあるパターン(例:省略可能・繰り返し・非終端の定義のパラメータ化など)を記述するための追加記法を用います。このセクションでは省略記法および文脈自由文法での展開定義について説明します。

制約

文法生成規則は「but not」(ただし...を除く)という表現を使い、特定の展開を許可しないことを示す場合があります。

例えば、次の生成規則は、非終端SafeWordWordで置換できる任意の文字列だが、同じ文字列でSevenCarlinWordsも置換可能な場合は除外する、という意味です。

SafeWord
WordSevenCarlinWords

制約は「but not」以降に「or」で区切って複数の条件を列挙することもできます。

例:

NonBooleanName
Nametruefalse
先読み制約

文法生成規則は、特定の文字やトークンの直後に続いてはならないことを、NotAllowedのようなパターンで指定できます。先読み制約は文法の曖昧性を除去するためによく使われます。

次の例では、Letterlistが貪欲であることを明示しています。Wordの直後にさらにLetterが続くことはできません。

省略可能・リスト

下付きの「Symbolopt」は、シンボルを含む並びと含まない並びの2通りの省略記法です。

例として:

Sentence
NounVerbAdverbopt

これは次の省略形です

Sentence
NounVerbAdverb
NounVerb

下付きの「Symbollist」は、その記号の1回以上のリストを記述する省略記法で、再帰生成規則で表されます。

例として:

Book
CoverPagelistCover

これは次の省略形です

Book
CoverPage_listCover
Page_list
Page
パラメータ付き文法生成規則

シンボル定義の下付きパラメータ「SymbolParam」は、パラメータ名を付加した定義と付加しない定義の2種類の省略形です。同じ下付きが付いたシンボルはその定義バリエーションを表します。パラメータが「?」で始まる場合、同じパラメータを持つ場所でその形のシンボルが使われます。一部の並びは「[+Param]」「[~Param]」で条件付き追加・除外できます。

例として:

ExampleParam
A
BParam
CParam
ParamD
ParamE

これは次の省略形です

Example
A
B_param
C
E
Example_param
A
B_param
C_param
D

B.4文法の意味論

本仕様では、多くの文法生成規則の意味値をアルゴリズム手順のリストとして記述しています。

例えば、パーサーが文字列リテラルをどのように解釈するかは次のように記述されます:

StringValue
""
  1. 空のUnicode文字列を返す。
StringValue
  1. すべてのStringCharacterUnicode文字値の列を返す。

B.5アルゴリズム

本仕様では、静的・実行時意味論で用いるいくつかのアルゴリズムを記述しており、関数的な記法でその名前・引数・手順リストを概要として示します。各手順は他値への参照・条件判定・他アルゴリズムの呼び出し・引数に対する結果値の返却などを行います。

例として、次の例はFibonacciというアルゴリズムを記載しています。このアルゴリズムは引数numberの次のフィボナッチ数を返します:

Fibonacci(number)
  1. もしnumber0なら:
    1. 1を返す。
  2. もしnumber1なら:
    1. 2を返す。
  3. previousNumbernumber - 1とする。
  4. previousPreviousNumbernumber - 2とする。
  5. Fibonacci(previousNumber) + Fibonacci(previousPreviousNumber)を返す。
注意 本書で記載されるアルゴリズムは分かりやすさを重視しています。実装者は、知覚上同等で最適化された実装を含めることが推奨されます。

B.6データコレクション

本仕様のアルゴリズムでは抽象的なデータコレクション型を参照し、構造・一意性・順序等の規範的要件を表現しています。アルゴリズムの一時的な内部コレクションでは、期待される挙動を最も分かりやすく記述できる型を利用していますが、実装者は同等で最適化された実装を利用することが推奨されます。期待される要件を満たす限り、どのようなデータ構造で実装しても構いません。

リスト

リストは順序付きの値の集まりで、重複を含み得ます。リストに値を追加すると、既存値の後に順序付けされます。

セット

セットは、重複を含んではならない値の集まりです。

順序付きセットは、順序の定義されたセットです。順序付きセットに値を追加すると、まだ同じ値が含まれていなければ、既存値の後に順序付けされます。

マップ

マップは、キーと値からなるエントリの集まりです。各エントリは一意なキーを持ち、そのキーによって直接参照できます。

順序付きマップは、順序の定義されたマップです。順序付きマップにエントリを追加すると、同じキーのエントリがなければ既存エントリの後に追加されます。

注意 本仕様では厳密に必要な場合のみ順序付きデータコレクションを定義します。順序が観測できる場合、実装は出力の可読性や安定性を高めるため順序を保持すべきです。例えば、入力文字列に文法を適用してセット要素を得た場合、その要素をシリアライズ時に元のソース順に出力するべきです。

C付録:文法概要

C.1ソーステキスト

SourceCharacter
任意の Unicode スカラー値

C.2無視されるトークン

UnicodeBOM
バイト順マーク (U+FEFF)
Whitespace
横タブ (U+0009)
スペース (U+0020)
LineTerminator
改行 (U+000A)
キャリッジリターン (U+000D)改行 (U+000A)
キャリッジリターン (U+000D)改行 (U+000A)
Comma
,

C.3字句トークン

Punctuator
! $ & ( ) ... : = @ [ ] { | }
Letter
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m
n o p q r s t u v w x y z
Digit
0 1 2 3 4 5 6 7 8 9
Sign
+ -
HexDigit
0 1 2 3 4 5 6 7 8 9
A B C D E F
a b c d e f
EscapedCharacter
" \ / b f n r t
Note ブロック文字列値は、先頭と末尾の空行を除去し、均一なインデントを取り除いた形で解釈されます(BlockStringValue()参照)。

C.4ドキュメント構文

OperationType
query mutation subscription
ArgumentsConst
(ArgumentConstlist)
ArgumentConst
Name:ValueConst
BooleanValue
true false
NullValue
null
EnumValue
Nametruefalsenull
ListValueConst
[]
[ValueConstlist]
ObjectValueConst
{}
{ObjectFieldConstlist}
ObjectFieldConst
Name:ValueConst
DirectivesConst
DirectiveConstlist
DirectiveConst
@NameArgumentsConstopt
SchemaExtension
extendschemaDirectivesConstopt{RootOperationTypeDefinitionlist}
extendschemaDirectivesConst{
ExecutableDirectiveLocation
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
TypeSystemDirectiveLocation
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION

C.5スキーマ座標構文

Note スキーマ座標は Ignored を含んではなりません。

D付録:型システムの定義

この付録には、本書で定められたすべての型システム定義が一覧されています。

型、フィールド、引数、値、およびディレクティブの順序は規範的ではありません。

scalar String

scalar Int

scalar Float

scalar Boolean

scalar ID

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @deprecated(
  reason: String! = "No longer supported"
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE

directive @specifiedBy(url: String!) on SCALAR

directive @oneOf on INPUT_OBJECT

type __Schema {
  description: String
  types: [__Type!]!
  queryType: __Type!
  mutationType: __Type
  subscriptionType: __Type
  directives: [__Directive!]!
}

type __Type {
  kind: __TypeKind!
  name: String
  description: String
  specifiedByURL: String
  fields(includeDeprecated: Boolean! = false): [__Field!]
  interfaces: [__Type!]
  possibleTypes: [__Type!]
  enumValues(includeDeprecated: Boolean! = false): [__EnumValue!]
  inputFields(includeDeprecated: Boolean! = false): [__InputValue!]
  ofType: __Type
  isOneOf: Boolean
}

enum __TypeKind {
  SCALAR
  OBJECT
  INTERFACE
  UNION
  ENUM
  INPUT_OBJECT
  LIST
  NON_NULL
}

type __Field {
  name: String!
  description: String
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
  type: __Type!
  isDeprecated: Boolean!
  deprecationReason: String
}

type __InputValue {
  name: String!
  description: String
  type: __Type!
  defaultValue: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __EnumValue {
  name: String!
  description: String
  isDeprecated: Boolean!
  deprecationReason: String
}

type __Directive {
  name: String!
  description: String
  isRepeatable: Boolean!
  locations: [__DirectiveLocation!]!
  args(includeDeprecated: Boolean! = false): [__InputValue!]!
}

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
}

§インデックス

  1. Alias
  2. AreTypesCompatible
  3. Argument
  4. ArgumentCoordinate
  5. Arguments
  6. ArgumentsDefinition
  7. BlockString
  8. BlockStringCharacter
  9. BlockStringValue()
  10. BooleanValue
  11. built-in directive
  12. CoerceArgumentValues
  13. CoerceResult
  14. CoerceVariableValues
  15. collected fields map
  16. CollectFields
  17. CollectSubfields
  18. CollectSubscriptionFields
  19. Comma
  20. Comment
  21. CommentChar
  22. CompleteValue
  23. containing element
  24. CreateSourceEventStream
  25. custom directive
  26. default root type name
  27. DefaultValue
  28. Definition
  29. Description
  30. DetectFragmentCycles
  31. Digit
  32. Directive
  33. DirectiveArgumentCoordinate
  34. DirectiveCoordinate
  35. DirectiveDefinition
  36. DirectiveLocation
  37. DirectiveLocations
  38. Directives
  39. Document
  40. DoesFragmentTypeApply
  41. EnumTypeDefinition
  42. EnumTypeExtension
  43. EnumValue
  44. EnumValueDefinition
  45. EnumValuesDefinition
  46. EscapedCharacter
  47. EscapedUnicode
  48. event stream
  49. ExecutableDefinition
  50. ExecutableDirectiveLocation
  51. ExecutableDocument
  52. ExecuteCollectedFields
  53. ExecuteField
  54. ExecuteMutation
  55. ExecuteQuery
  56. ExecuteRequest
  57. ExecuteRootSelectionSet
  58. ExecuteSubscriptionEvent
  59. execution error
  60. execution result
  61. ExponentIndicator
  62. ExponentPart
  63. Field
  64. field set
  65. FieldDefinition
  66. FieldsDefinition
  67. FieldsInSetCanMerge
  68. FloatValue
  69. FractionalPart
  70. FragmentDefinition
  71. FragmentName
  72. FragmentSpread
  73. GetOperation
  74. GetPossibleTypes
  75. HexDigit
  76. Ignored
  77. ImplementsInterfaces
  78. InlineFragment
  79. Input Object
  80. InputFieldDefaultValueHasCycle
  81. InputFieldsDefinition
  82. InputObjectDefaultValueHasCycle
  83. InputObjectTypeDefinition
  84. InputObjectTypeExtension
  85. InputValueDefinition
  86. IntegerPart
  87. InterfaceTypeDefinition
  88. InterfaceTypeExtension
  89. IntValue
  90. IsInputType
  91. IsNonNullPosition
  92. IsOutputType
  93. IsSubType
  94. IsValidImplementation
  95. IsValidImplementationFieldType
  96. IsVariableUsageAllowed
  97. Letter
  98. LineTerminator
  99. ListType
  100. ListValue
  101. MapSourceToResponseEvent
  102. MemberCoordinate
  103. Name
  104. NameContinue
  105. NamedType
  106. NameStart
  107. NegativeSign
  108. NonNullType
  109. NonZeroDigit
  110. NullValue
  111. ObjectField
  112. ObjectTypeDefinition
  113. ObjectTypeExtension
  114. ObjectValue
  115. OneOf Input Object
  116. OperationDefinition
  117. OperationType
  118. Punctuator
  119. request
  120. request error
  121. request error result
  122. ResolveAbstractType
  123. ResolveFieldEventStream
  124. ResolveFieldValue
  125. response
  126. response name
  127. response path
  128. response position
  129. response stream
  130. root operation type
  131. root selection set
  132. RootOperationTypeDefinition
  133. SameResponseShape
  134. scalar specification URL
  135. ScalarTypeDefinition
  136. ScalarTypeExtension
  137. schema coordinate
  138. schema element
  139. SchemaCoordinate
  140. SchemaCoordinatePunctuator
  141. SchemaCoordinateToken
  142. SchemaDefinition
  143. SchemaExtension
  144. Selection
  145. selection set
  146. SelectionSet
  147. Sign
  148. source stream
  149. SourceCharacter
  150. StringCharacter
  151. StringValue
  152. Subscribe
  153. Token
  154. Type
  155. TypeCondition
  156. TypeCoordinate
  157. TypeDefinition
  158. TypeExtension
  159. TypeSystemDefinition
  160. TypeSystemDefinitionOrExtension
  161. TypeSystemDirectiveLocation
  162. TypeSystemDocument
  163. TypeSystemExtension
  164. TypeSystemExtensionDocument
  165. Unicode text
  166. UnicodeBOM
  167. UnionMemberTypes
  168. UnionTypeDefinition
  169. UnionTypeExtension
  170. Unsubscribe
  171. Value
  172. Variable
  173. VariableDefinition
  174. VariablesDefinition
  175. Whitespace
  1. 1概要
  2. 2言語
    1. 2.1ソーステキスト
      1. 2.1.1空白
      2. 2.1.2行終端
      3. 2.1.3コメント
      4. 2.1.4不要なカンマ
      5. 2.1.5字句トークン
      6. 2.1.6無視トークン
      7. 2.1.7句読点
      8. 2.1.8名前
    2. 2.2説明
    3. 2.3ドキュメント
    4. 2.4操作
    5. 2.5選択セット
    6. 2.6フィールド
    7. 2.7引数
    8. 2.8フィールドエイリアス
    9. 2.9フラグメント
      1. 2.9.1型条件
      2. 2.9.2インラインフラグメント
    10. 2.10入力値
      1. 2.10.1Int値
      2. 2.10.2Float値
      3. 2.10.3Boolean 値
      4. 2.10.4文字列値
      5. 2.10.5Null値
      6. 2.10.6列挙値
      7. 2.10.7リスト値
      8. 2.10.8入力オブジェクト値
    11. 2.11変数
    12. 2.12型参照
    13. 2.13ディレクティブ
    14. 2.14スキーマ座標
  3. 3型システム
    1. 3.1型システム拡張
    2. 3.2型システムの説明
    3. 3.3スキーマ
      1. 3.3.1ルート操作型
      2. 3.3.2スキーマ拡張
    4. 3.4
      1. 3.4.1ラップ型
      2. 3.4.2入力・出力型
      3. 3.4.3型拡張
    5. 3.5スカラー
      1. 3.5.1Int
      2. 3.5.2Float
      3. 3.5.3String
      4. 3.5.4Boolean
      5. 3.5.5ID
      6. 3.5.6スカラー拡張
    6. 3.6オブジェクト
      1. 3.6.1フィールド引数
      2. 3.6.2フィールドの非推奨化
      3. 3.6.3オブジェクト拡張
    7. 3.7インターフェース
      1. 3.7.1インターフェース拡張
    8. 3.8ユニオン
      1. 3.8.1ユニオン拡張
    9. 3.9列挙型
      1. 3.9.1列挙型拡張
    10. 3.10入力オブジェクト
      1. 3.10.1OneOf 入力オブジェクト
      2. 3.10.2入力オブジェクト拡張
    11. 3.11リスト
    12. 3.12非Null
      1. 3.12.1リストと非Nullの組み合わせ
    13. 3.13ディレクティブ
      1. 3.13.1@skip
      2. 3.13.2@include
      3. 3.13.3@deprecated
      4. 3.13.4@specifiedBy
      5. 3.13.5@oneOf
  4. 4イントロスペクション
    1. 4.1型名のイントロスペクション
    2. 4.2スキーマのイントロスペクション
      1. 4.2.1__Schema 型
      2. 4.2.2__Type 型
      3. 4.2.3__Field 型
      4. 4.2.4__InputValue 型
      5. 4.2.5__EnumValue 型
      6. 4.2.6__Directive 型
  5. 5検証
    1. 5.1ドキュメント
      1. 5.1.1実行可能定義
    2. 5.2操作
      1. 5.2.1すべての操作定義
        1. 5.2.1.1操作型の存在
      2. 5.2.2名前付き操作定義
        1. 5.2.2.1操作名の一意性
      3. 5.2.3匿名操作定義
        1. 5.2.3.1単独の匿名操作
      4. 5.2.4サブスクリプション操作定義
        1. 5.2.4.1単一ルートフィールド
    3. 5.3フィールド
      1. 5.3.1フィールド選択
      2. 5.3.2フィールド選択のマージ
      3. 5.3.3リーフフィールド選択
    4. 5.4引数
      1. 5.4.1引数名
      2. 5.4.2引数の一意性
      3. 5.4.3必須引数
    5. 5.5フラグメント
      1. 5.5.1フラグメント宣言
        1. 5.5.1.1フラグメント名の一意性
        2. 5.5.1.2フラグメントの型の存在
        3. 5.5.1.3オブジェクト・インターフェース・ユニオン上のフラグメント
        4. 5.5.1.4フラグメントは使用されねばならない
      2. 5.5.2フラグメントスプレッド
        1. 5.5.2.1スプレッド先の定義
        2. 5.5.2.2フラグメントスプレッドはサイクルを形成してはならない
        3. 5.5.2.3フラグメントスプレッドが可能であること
          1. 5.5.2.3.1オブジェクトスコープ内でのオブジェクトスプレッド
          2. 5.5.2.3.2オブジェクトスコープ内での抽象スプレッド
          3. 5.5.2.3.3抽象スコープ内でのオブジェクトスプレッド
          4. 5.5.2.3.4抽象スコープ内の抽象スプレッド
    6. 5.6
      1. 5.6.1正しい型の値
      2. 5.6.2入力オブジェクトのフィールド名
      3. 5.6.3入力オブジェクトフィールドの一意性
      4. 5.6.4入力オブジェクトの必須フィールド
    7. 5.7ディレクティブ
      1. 5.7.1ディレクティブが定義されていること
      2. 5.7.2ディレクティブが有効な場所にあること
      3. 5.7.3ディレクティブは位置ごとに一意であること
    8. 5.8変数
      1. 5.8.1変数の一意性
      2. 5.8.2変数は入力型であること
      3. 5.8.3すべての変数使用が定義されていること
      4. 5.8.4すべての変数が使用されていること
      5. 5.8.5すべての変数使用が許容されること
  6. 6実行
    1. 6.1リクエストの実行
      1. 6.1.1リクエストの検証
      2. 6.1.2変数値の強制変換
    2. 6.2操作の実行
      1. 6.2.1クエリ
      2. 6.2.2ミューテーション
      3. 6.2.3サブスクリプション
        1. 6.2.3.1ソースストリーム
        2. 6.2.3.2応答ストリーム
        3. 6.2.3.3解除
    3. 6.3選択セットの実行
      1. 6.3.1ルート選択セットの実行
      2. 6.3.2フィールド収集
      3. 6.3.3収集されたフィールドの実行
      4. 6.3.4通常実行と直列実行
    4. 6.4フィールドの実行
      1. 6.4.1フィールド引数の強制型変換
      2. 6.4.2値の解決
      3. 6.4.3値の完了
      4. 6.4.4実行エラーの処理
  7. 7レスポンス
    1. 7.1レスポンスフォーマット
      1. 7.1.1実行結果
      2. 7.1.2レスポンスストリーム
      3. 7.1.3リクエストエラー結果
      4. 7.1.4レスポンスポジション
      5. 7.1.5データ
      6. 7.1.6エラー
      7. 7.1.7拡張
      8. 7.1.8追加エントリ
    2. 7.2シリアライズ形式
      1. 7.2.1JSON シリアライズ
      2. 7.2.2シリアライズ済みマップの順序
  8. A付録:適合性
  9. B付録:記法の慣例
    1. B.1非文脈自由文法
    2. B.2字句・構文文法
    3. B.3文法記法
    4. B.4文法の意味論
    5. B.5アルゴリズム
    6. B.6データコレクション
  10. C付録:文法概要
    1. C.1ソーステキスト
    2. C.2無視されるトークン
    3. C.3字句トークン
    4. C.4ドキュメント構文
    5. C.5スキーマ座標構文
  11. D付録:型システムの定義
  12. E付録:著作権とライセンス
  13. §インデックス