GraphQL LogoGraphQL

クエリとミューテーション

このページでは、GraphQLサーバーにクエリを実行する方法について詳しく説明します。

フィールド#

最も単純な場合、GraphQLはオブジェクトの特定のフィールドを要求することです。非常に単純なクエリと、それを実行したときに得られる結果を見てみましょう。

クエリと結果がまったく同じ形状であることがすぐにわかります。これはGraphQLにとって不可欠です。常に期待どおりのものが返され、サーバーはクライアントが要求しているフィールドを正確に把握できるためです。

フィールドnameString型を返し、この場合はスターウォーズの主人公の名前である"R2-D2"です。

ああ、もう1つ。上記のクエリはインタラクティブです。つまり、自由にそれを変更して新しい結果を確認できます。クエリ内のheroオブジェクトにappearsInフィールドを追加して、新しい結果を確認してください。

前の例では、文字列を返すヒーローの名前を要求しただけでしたが、フィールドはオブジェクトを参照することもできます。その場合、そのオブジェクトのフィールドのサブ選択を行うことができます。GraphQLクエリは、関連オブジェクトとそのフィールドをトラバースできるため、クライアントは古典的なRESTアーキテクチャのように複数のラウンドトリップを行う代わりに、1回の要求で多くの関連データを取得できます。

この例では、friendsフィールドが項目の配列を返すことに注意してください。GraphQLクエリは、単一の項目でも項目のリストでも同じように見えます。ただし、スキーマに示されている内容に基づいて、どちらが期待されるかを知っています。

引数#

オブジェクトとそのフィールドをトラバースすることしかできないとしたら、GraphQLはすでにデータの取得に非常に役立つ言語でしょう。しかし、フィールドに引数を渡す機能を追加すると、状況はさらに面白くなります。

RESTのようなシステムでは、リクエスト内のクエリパラメータとURLセグメントという、単一の引数セットのみを渡すことができます。しかし、GraphQLでは、すべてのフィールドとネストされたオブジェクトが独自の引数セットを取得できるため、GraphQLは複数のAPIフェッチを行うための完全な代替手段となります。スカラーフィールドに引数を渡して、すべてのクライアントで個別にではなく、サーバー上で一度だけデータ変換を実装することもできます。

引数には、さまざまな型を使用できます。上記の例では、有限のオプションセット(この場合、長さの単位、METERまたはFOOTのいずれか)を表す列挙型を使用しました。GraphQLにはデフォルトの型セットが付属していますが、GraphQLサーバーは、トランスポート形式にシリアル化できる限り、独自のカスタム型を宣言することもできます。

GraphQL型システムの詳細については、こちらをご覧ください。

エイリアス#

鋭い目をお持ちの方は、結果オブジェクトのフィールドがクエリ内のフィールドの名前と一致し、引数が含まれていないため、異なる引数を持つ同じフィールドを直接クエリできないことに気づいたかもしれません。そのため、エイリアスが必要です。これにより、フィールドの結果を好きな名前に変更できます。

上記の例では、2つのheroフィールドが競合していましたが、異なる名前にエイリアスできるため、1つのリクエストで両方の結果を得ることができます。

フラグメント#

アプリに、2人のヒーローを並べて表示し、彼らの友人を見ることを可能にする比較的複雑なページがあるとしましょう。このようなクエリは、フィールドを少なくとも一度繰り返す必要があるため、すぐに複雑になる可能性があると想像できます。比較の両側で1つずつです。

そのため、GraphQLにはフラグメントと呼ばれる再利用可能なユニットが含まれています。フラグメントを使用すると、フィールドのセットを構築し、必要な場合にクエリに含めることができます。フラグメントを使用して上記の状況を解決する方法の例を次に示します。

フィールドが繰り返されると、上記のクエリが非常に反復的になる可能性があることがわかります。フラグメントの概念は、特に多数のUIコンポーネントを異なるフラグメントと組み合わせて1つの初期データフェッチにまとめる必要がある場合に、複雑なアプリケーションデータ要件をより小さなチャンクに分割するためによく使用されます。

フラグメント内で変数を使用する#

フラグメントがクエリまたはミューテーションで宣言された変数にアクセスすることが可能です。変数を参照してください。

操作名#

上記の例のいくつかでは、queryキーワードとクエリ名の両方を省略する省略構文を使用しましたが、本番アプリでは、コードの曖昧さを減らすためにこれらを使用すると便利です。

キーワードquery操作タイプとして、HeroNameAndFriends操作名として含む例を次に示します。

操作タイプは、クエリミューテーション、またはサブスクリプションのいずれかで、実行しようとしている操作の種類を記述します。操作タイプは、クエリ省略構文を使用している場合を除き、必須です。その場合、操作の名前または変数定義を指定することはできません。

操作名は、操作の有意義で明示的な名前です。これは複数操作ドキュメントでのみ必須ですが、デバッグとサーバー側のロギングに非常に役立つため、その使用が推奨されます。何か問題が発生した場合(ネットワークログまたはGraphQLサーバーのログにエラーが表示された場合)、内容を解読しようとする代わりに、名前でコードベース内のクエリを識別する方が簡単です。これを、お気に入りのプログラミング言語の関数名のように考えてください。たとえば、JavaScriptでは、匿名関数のみを使用するのは簡単ですが、関数に名前を付けると、その関数を追跡し、コードをデバッグし、いつ呼び出されたかをログに記録するのが簡単になります。同様に、GraphQLのクエリとミューテーションの名前は、フラグメント名とともに、サーバー側でさまざまなGraphQLリクエストを識別するのに役立つデバッグツールになります。

変数#

これまで、すべての引数をクエリ文字列内に記述してきました。しかし、ほとんどのアプリケーションでは、フィールドへの引数は動的になります。たとえば、目的のスターウォーズのエピソードを選択できるドロップダウン、検索フィールド、またはフィルターのセットなどがある場合があります。

これらの動的な引数をクエリ文字列に直接渡すのは良い考えではありません。クライアント側のコードで実行時にクエリ文字列を動的に操作し、GraphQL固有の形式にシリアル化する必要があるためです。代わりに、GraphQLには、クエリから動的な値を因数分解し、別の辞書として渡すためのファーストクラスの方法があります。これらの値は変数と呼ばれます。

変数を使用する場合、3つのことを行う必要があります。

  1. クエリ内の静的な値を$variableNameに置き換える
  2. $variableNameをクエリで受け入れられる変数の1つとして宣言する
  3. 個別のトランスポート固有の(通常はJSON)変数辞書にvariableName: valueを渡す

すべてをまとめると、次のようになります。

これで、クライアントコードでは、まったく新しいクエリを作成するのではなく、異なる変数を渡すだけで済みます。これは、クエリでどの引数が動的であると予想されるかを示すための一般的な良い方法でもあります。ユーザーが指定した値からクエリを作成するために、文字列の補間を行うべきではありません。

変数定義#

変数定義は、上記のクエリの($episode: Episode)のように見える部分です。これは、型付き言語の関数の引数定義と同じように機能します。これは、すべての変数を$でプレフィックスを付け、その後に型(この場合はEpisode)を一覧表示します。

宣言されたすべての変数は、スカラー、enum、または入力オブジェクト型である必要があります。したがって、複雑なオブジェクトをフィールドに渡す場合は、サーバー上でどの入力型が一致するかを知る必要があります。入力オブジェクト型の詳細については、スキーマページを参照してください。

変数定義は、オプションまたは必須にできます。上記の場合、Episode型の横に!がないため、オプションです。ただし、変数を渡すフィールドが非null引数を必要とする場合、変数も必須である必要があります。

これらの変数定義の構文の詳細については、GraphQLスキーマ言語を学ぶと役立ちます。スキーマ言語の詳細については、スキーマページで説明しています。

デフォルト変数#

型宣言の後にデフォルト値を追加することにより、クエリ内の変数にデフォルト値を割り当てることもできます。

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

すべての変数にデフォルト値が提供されている場合は、変数を渡さずにクエリを呼び出すことができます。変数の辞書の一部として変数が渡された場合、それらはデフォルトを上書きします。

ディレクティブ#

上記では、変数が動的クエリを作成するための手動の文字列補間を回避する方法について説明しました。引数に変数を渡すと、これらの問題の非常に大きなクラスが解決されますが、変数を使用してクエリの構造と形状を動的に変更する方法も必要になる場合があります。たとえば、1つがもう1つよりも多くのフィールドを含む、要約されたビューと詳細なビューを持つUIコンポーネントを想像できます。

このようなコンポーネントのクエリを作成しましょう

上記の変数を編集して、代わりにwithFriendstrueを渡すと、結果がどのように変化するかを確認してください。

GraphQLでディレクティブと呼ばれる新しい機能を使用する必要がありました。ディレクティブは、フィールドまたはフラグメントの包含にアタッチでき、サーバーが必要とする任意の方法でクエリの実行に影響を与える可能性があります。コアGraphQL仕様には、仕様に準拠したGraphQLサーバーの実装でサポートする必要がある、正確に2つのディレクティブが含まれています。

  • @include(if: Boolean) 引数がtrueの場合にのみ、このフィールドを結果に含めます。
  • @skip(if: Boolean) 引数がtrueの場合、このフィールドをスキップします。

ディレクティブは、クエリ内のフィールドを追加および削除するために、文字列操作を行う必要のある状況から抜け出すのに役立ちます。サーバー実装では、完全に新しいディレクティブを定義することにより、実験的な機能を追加することもできます。

ミューテーション#

GraphQLの議論のほとんどはデータのフェッチに焦点を当てていますが、完全なデータプラットフォームにはサーバー側のデータを変更する方法も必要です。

RESTでは、リクエストがサーバー上でいくつかの副作用を引き起こす可能性がありますが、慣例により、データを変更するためにGETリクエストを使用しないことが推奨されています。GraphQLも同様です。技術的には、任意のクエリを実装してデータの書き込みを引き起こすことができます。ただし、書き込みを引き起こすすべての操作は、明示的にミューテーションを介して送信する必要があるという規則を確立すると便利です。

クエリと同様に、ミューテーションフィールドがオブジェクト型を返す場合は、ネストされたフィールドを要求できます。これは、更新後のオブジェクトの新しい状態をフェッチするのに役立ちます。簡単なミューテーションの例を見てみましょう。

createReviewフィールドが、新しく作成されたレビューのstarsおよびcommentaryフィールドを返していることに注意してください。これは、たとえば、フィールドをインクリメントする場合など、既存のデータをミューテートする場合に特に役立ちます。1つのリクエストでフィールドの新しい値をミューテートおよびクエリできるためです。

この例では、渡した review 変数がスカラではないことにも気づくかもしれません。これは入力オブジェクト型であり、引数として渡すことができる特別なオブジェクト型です。入力型についての詳細は、スキーマのページで確認してください。

ミューテーション内の複数のフィールド#

ミューテーションには、クエリと同様に複数のフィールドを含めることができます。クエリとミューテーションの間には、名前以外にも重要な違いが1つあります。

クエリのフィールドは並行して実行されますが、ミューテーションのフィールドは直列に、1つずつ実行されます。

これは、1つのリクエストで2つの incrementCredits ミューテーションを送信した場合、最初のミューテーションが完了してから2番目のミューテーションが開始されることが保証され、競合状態が発生しないようにすることを意味します。

インラインフラグメント#

他の多くの型システムと同様に、GraphQLスキーマにはインターフェースとユニオン型を定義する機能が含まれています。スキーマガイドで詳細をご覧ください。

インターフェースまたはユニオン型を返すフィールドをクエリする場合は、基礎となる具象型に関するデータにアクセスするためにインラインフラグメントを使用する必要があります。例を見るのが一番簡単です。

このクエリでは、hero フィールドは Character 型を返しますが、これは episode 引数に応じて Human または Droid のいずれかになります。直接選択では、name など Character インターフェースに存在するフィールドのみを要求できます。

具象型のフィールドを要求するには、型条件付きのインラインフラグメントを使用する必要があります。最初のフラグメントが ... on Droid としてラベル付けされているため、hero から返された CharacterDroid 型の場合にのみ、primaryFunction フィールドが実行されます。同様に、Human 型の場合は height フィールドが実行されます。

名前付きフラグメントには常に型が関連付けられているため、名前付きフラグメントも同様に使用できます。

メタフィールド#

GraphQLサービスから返される型がわからない場合があるため、クライアントでそのデータをどのように処理するかを判断する方法が必要です。GraphQLでは、クエリの任意の場所でメタフィールドである __typename を要求して、その時点のオブジェクト型の名前を取得できます。

上記のクエリでは、search は3つのオプションのいずれかになりうるユニオン型を返します。__typename フィールドなしでは、クライアントから異なる型を区別することは不可能でしょう。

GraphQLサービスはいくつかのメタフィールドを提供しており、残りはイントロスペクションシステムを公開するために使用されます。

続きを読む →スキーマと型