このページでは、GraphQLの型システムとそのデータクエリ記述方法について、必要な情報をすべて学習します。GraphQLはあらゆるバックエンドフレームワークやプログラミング言語で使用できるため、実装固有の詳細には触れず、概念のみに焦点を当てて説明します。
GraphQLクエリを以前に見たことがある場合は、GraphQLクエリ言語は基本的にオブジェクト上のフィールドを選択することであることをご存知でしょう。そのため、次のクエリでは
hero
フィールドを選択します。hero
によって返されるオブジェクトに対して、name
とappearsIn
フィールドを選択します。GraphQLクエリの形状は結果と密接に一致するため、サーバーについて詳しく知らなくても、クエリが返すものを予測できます。しかし、要求できるデータの正確な記述(選択できるフィールド、返される可能性のあるオブジェクトの種類、それらのサブオブジェクトで使用できるフィールドなど)があると便利です。それがスキーマの役割です。
すべてのGraphQLサービスは、そのサービスでクエリできる可能性のあるデータセットを完全に記述する一連の型を定義します。その後、クエリが到着すると、そのスキーマに対して検証および実行されます。
GraphQLサービスは任意の言語で記述できます。GraphQLスキーマについて説明するために、JavaScriptのような特定のプログラミング言語の構文に依存できないため、独自のシンプルな言語を定義します。「GraphQLスキーマ言語」を使用します。これはクエリ言語に似ており、言語に依存しない方法でGraphQLスキーマについて説明できます。
GraphQLスキーマの最も基本的なコンポーネントはオブジェクト型です。これは、サービスからフェッチできるオブジェクトの種類とそのフィールドを表します。GraphQLスキーマ言語では、次のように表すことができます。
type Character { name: String! appearsIn: [Episode!]!}
言語は非常に読みやすいですが、共通の語彙を持つために確認しましょう。
Character
はGraphQLオブジェクト型です。つまり、いくつかのフィールドを持つ型です。スキーマ内の型のほとんどはオブジェクト型です。name
とappearsIn
はCharacter
型のフィールドです。つまり、name
とappearsIn
は、Character
型を操作するGraphQLクエリの任意の部分に表示できる唯一のフィールドです。String
は組み込みのスカラー型の1つです。これらは単一のスカラーオブジェクトに解決される型であり、クエリでサブセレクションを持つことはできません。スカラー型については後で詳しく説明します。String!
は、フィールドが非NULLであることを意味します。つまり、GraphQLサービスは、このフィールドをクエリすると常に値を提供することを約束します。型言語では、感嘆符を使用して表します。[Episode!]!
はEpisode
オブジェクトの配列を表します。これも非NULLであるため、appearsIn
フィールドをクエリするときは常に(0個以上のアイテムを含む)配列を期待できます。また、Episode!
も非NULLであるため、配列の各アイテムがEpisode
オブジェクトであることを常に期待できます。これで、GraphQLオブジェクト型の外観とGraphQL型言語の基本的な読み方について理解できました。
GraphQLオブジェクト型のすべてのフィールドには、0個以上の引数を持つことができます。たとえば、次のlength
フィールド。
type Starship { id: ID! name: String! length(unit: LengthUnit = METER): Float}
すべての引数には名前が付けられています。関数が順序付けられた引数のリストを受け取るJavaScriptやPythonなどの言語とは異なり、GraphQLのすべての引数は名前で具体的に渡されます。この場合、length
フィールドには、unit
という1つの定義済みの引数があります。
引数は必須またはオプションのいずれかです。引数がオプションの場合、デフォルト値を定義できます。unit
引数が渡されない場合、デフォルトでMETER
に設定されます。
スキーマ内のほとんどの型は通常のオブジェクト型ですが、スキーマ内で特別な2つの型があります。
schema { query: Query mutation: Mutation}
すべてのGraphQLサービスにはquery
型があり、mutation
型がある場合とない場合があります。これらの型は通常のオブジェクト型と同じですが、すべてのGraphQLクエリのエントリポイントを定義するため特別です。そのため、次のようなクエリを見ると
GraphQLサービスには、hero
とdroid
フィールドを持つQuery
型が必要です。
type Query { hero(episode: Episode): Character droid(id: ID!): Droid}
Mutationは同様の方法で機能します。Mutation
型にフィールドを定義すると、それらはクエリで呼び出すことができるルートmutationフィールドとして使用できます。
「エントリポイント」であるという特別なステータス以外では、Query
型とMutation
型は他のGraphQLオブジェクト型と同じであり、それらのフィールドはまったく同じように機能することを覚えておくことが重要です。
GraphQLオブジェクト型には名前とフィールドがありますが、ある時点でそれらのフィールドは具体的なデータに解決される必要があります。そこでスカラー型が登場します。これらはクエリのリーフを表します。
次のクエリでは、name
とappearsIn
フィールドはスカラー型に解決されます。
これらのフィールドにはサブフィールドがないため、クエリのリーフであることがわかります。
GraphQLには、すぐに使用できるデフォルトのスカラー型のセットが用意されています。
Int
:符号付き32ビット整数。Float
:符号付き倍精度浮動小数点値。String
:UTF‐8文字列。Boolean
:true
またはfalse
。ID
:IDスカラー型は一意の識別子を表現し、多くの場合、オブジェクトを再フェッチするため、またはキャッシュのキーとして使用されます。ID型はStringと同じ方法でシリアル化されますが、ID
として定義すると、人間が読み取ることを意図していないことが示されます。ほとんどのGraphQLサービス実装では、カスタムスカラー型を指定する方法もあります。たとえば、Date
型を定義できます。
scalar Date
次に、その型のシリアル化、デシリアル化、検証方法を実装で定義します。たとえば、Date
型は常に整数タイムスタンプにシリアル化されるように指定し、クライアントはすべての日付フィールドでその形式を期待するようにする必要があります。
Enumとも呼ばれる列挙型は、特定の許可値セットに制限された特殊なスカラー型です。これにより、以下を行うことができます。
GraphQLスキーマ言語でのenum定義の例を次に示します。
enum Episode { NEWHOPE EMPIRE JEDI}
これは、スキーマでEpisode
型を使用する場合は常に、NEWHOPE
、EMPIRE
、またはJEDI
のいずれか1つであることを意味します。
さまざまな言語のGraphQLサービス実装には、enumを処理するための独自の言語固有の方法があることに注意してください。enumをファーストクラスの市民としてサポートする言語では、実装はその機能を利用する可能性があります。enumをサポートしないJavaScriptのような言語では、これらの値は内部的に整数のセットにマッピングされる可能性があります。ただし、これらの詳細はクライアントには漏れません。クライアントはenum値の文字列名で完全に動作できます。
オブジェクト型、スカラー、enumは、GraphQLで定義できる唯一の種類の型です。しかし、スキーマの他の部分やクエリの変数宣言で使用する場合、それらの値の検証に影響を与える追加の型修飾子を適用できます。例を見てみましょう。
type Character { name: String! appearsIn: [Episode]!}
ここでは、String
型を使用し、型名の後に感嘆符!
を追加することで、非NULLとしてマークしています。これは、サーバーがこのフィールドに対して常に非NULL値を返すことを期待しており、最終的にNULL値を取得すると、実際にGraphQL実行エラーが発生し、クライアントに何かが間違っていることを知らせることを意味します。
非NULL型修飾子は、フィールドの引数を定義する場合にも使用でき、GraphQL文字列または変数のいずれかでNULL値が引数として渡された場合、GraphQLサーバーは検証エラーを返します。
リストは同様の方法で機能します。型修飾子を使用して型をList
としてマークできます。これは、このフィールドがその型の配列を返すことを示します。スキーマ言語では、これは型を角括弧[
と]
で囲むことで示されます。引数の場合も同様に機能し、検証ステップではその値に配列が期待されます。
非NULLとListの修飾子は組み合わせることができます。たとえば、非NULL文字列のリストを持つことができます。
myField: [String!]
これは、リスト自体がNULLになる可能性がありますが、NULLメンバーを持つことはできないことを意味します。たとえば、JSONでは
myField: null // validmyField: [] // validmyField: ["a", "b"] // validmyField: ["a", null, "b"] // error
ここで、非NULL文字列のリストを定義したとします。
myField: [String]!
これは、リスト自体がNULLになることはできませんが、NULL値を含むことができることを意味します。
myField: null // errormyField: [] // validmyField: ["a", "b"] // validmyField: ["a", null, "b"] // valid
必要に応じて、任意の数の非NULLとListの修飾子を自由にネストできます。
多くの型システムと同様に、GraphQLはインターフェースをサポートしています。インターフェースは、型がインターフェースを実装するために含める必要がある特定のフィールドセットを含む抽象型です。
たとえば、スターウォーズ三部作の任意のキャラクターを表すCharacter
インターフェースを持つことができます。
interface Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]!}
これは、Character
を実装する型には、これらの引数と戻り値型を持つこれらの正確なフィールドが含まれている必要があることを意味します。
たとえば、Character
を実装する可能性のある型をいくつか次に示します。
type Human implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! starships: [Starship] totalCredits: Int}
type Droid implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! primaryFunction: String}
これらの2つの型にはどちらもCharacter
インターフェースのすべてのフィールドがありますが、その特定のキャラクターの種類に固有のtotalCredits
、starships
、primaryFunction
などの追加フィールドも含まれています。
オブジェクトまたはオブジェクトセットを返す必要があるが、それらがいくつかの異なる型である可能性がある場合に、インターフェースは役立ちます。
たとえば、次のクエリはエラーを生成することに注意してください。
hero
フィールドはCharacter
型を返し、これはepisode
引数に応じてHuman
またはDroid
のいずれかである可能性があることを意味します。上記のクエリでは、Character
インターフェースに存在するフィールドのみを要求できますが、primaryFunction
は含まれていません。
特定のオブジェクト型のフィールドを要求するには、インラインフラグメントを使用する必要があります。
クエリガイドのインラインフラグメントセクションで詳細を確認してください。
ユニオン型はインターフェースと類似性がありますが、構成要素の型間で共有フィールドを定義する機能がありません。
union SearchResult = Human | Droid | Starship
スキーマでSearchResult
型を返す場合は常に、Human
、Droid
、またはStarship
を取得する可能性があります。ユニオン型のメンバーは具体的なオブジェクト型である必要があることに注意してください。インターフェースや他のユニオンからユニオン型を作成することはできません。
この場合、SearchResult
ユニオン型を返すフィールドをクエリする場合は、いかなるフィールドもクエリできるようにインラインフラグメントを使用する必要があります。
__typename
フィールドはString
に解決され、クライアント上で異なるデータ型を区別することができます。
また、この場合、Human
と Droid
は共通のインターフェース(Character
)を共有しているため、複数の型にわたって同じフィールドを繰り返す必要なく、一箇所で共通のフィールドをクエリできます。
{ search(text: "an") { __typename ... on Character { name } ... on Human { height } ... on Droid { primaryFunction } ... on Starship { name length } }}
Starship
はCharacter
ではないため、name
を指定しないと結果に表示されないことに注意してください!
これまで、列挙型や文字列などのスカラー値を引数としてフィールドに渡すことについてのみ説明してきました。しかし、複雑なオブジェクトも簡単に渡すことができます。これは、作成するオブジェクト全体を渡したい可能性のあるミューテーションの場合に特に役立ちます。GraphQL スキーマ言語では、入力型は通常のオブジェクト型とまったく同じように見えますが、type
の代わりにキーワード input
を使用します。
input ReviewInput { stars: Int! commentary: String}
ミューテーションで入力オブジェクト型を使用する方法を次に示します。
入力オブジェクト型のフィールドは、それ自体が入力オブジェクト型を参照できますが、スキーマ内で入力型と出力型を混在させることはできません。入力オブジェクト型は、フィールドに引数を持つこともできません。