コミュニティ

GraphQLのクエリを基礎から整理してみた

この記事は最終更新日から1年以上が経過しています。

この記事はGraphQLで使われる文法の入門編です。
よく使われるクエリの文法に焦点を当てて整理しています。

今回はPlaygroundとして一般公開されている以下のサイトを例にして、GraphQLの文法をクエリの実行を試しながら整理していきます。

http://snowtooth.moonhighway.com/
FireShot Capture 157 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

※以下のページより、PlaygroundのIDEをインストールして利用することも可能です。今回は既に公開されているスキーマベースで文法を確認していきます。
https://github.com/prisma/graphql-playground

SQL/RESTとの比較

GraphQLはクエリ言語です。基本的にはQueryとMutation、Subscriptionを使い分けます。

  • SQLはデータベース専用のクエリ言語です。
  • GraphQLはインターネットのためのクエリ言語です。
SQL/REST GraphQL
取得 SELECT Query
登録 INSERT Mutation
更新 UPDATE Mutation
削除 DELETE Mutation
  • Subscriptionはサーバサイドからプッシュ通知を受けるようなリアルタイム処理で使用されます。

RESTでデータを取得するときと同様に、curlによってデータを取得することができます。

curlでクエリを投げる方法
$ curl 'http://.../' -H 'Content-Type: application/json' --data '{"query": "{allLifts {name}}"}'

スキーマ

Playgloundからスキーマを確認してみます。
PlayGroundの右側にSCHEMAという緑色のタグがあるのでクリックすると、スキーマの定義を確認することができます。

QUERIES/MUTATIONS/SUBSCRIPTIONSと大別されていて、スキーマに定義されたクエリ等の情報が記載されています。

FireShot Capture 171 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

例えば、Liftオブジェクトの一覧を取得したい場合はallLiftsを使用すればよく、どのプロパティを取得できるかまで見ることが可能です。

下記でクエリの文法を見ていきます。

フィールド

クエリ取得できるフィールドは、スカラタイプとオブジェクトタイプの2種類が存在します。

スカラタイプはプログラミング言語でいうところのプリミティブ型を意味しており、GraphQLでは以下の5種類が対応しています。

  • Int
  • Float
  • String
  • Boolean
  • ID

※IDはユニークな文字列が返却されます。

オブジェクトタイプは1つ以上のフィールドを含んだグループで、JSONの入れ子として表現されます。

シンプルなQuery

データを取得する際の最も基本的なqueryです。allLiftsでLiftsの配列を要求し、配列内の要素としてnameを指定しています。allLiftsは上記のQUERIESで定義されているものです。

シンプルなクエリ①
query {
  allLifts {
    name
  }
}

このレスポンス結果は以下の通りです。配列の中にname要素のみが返却されていることがわかります。
FireShot Capture 158 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

配列内の要素としてstatusを追加してみます。リクエストに欲しい要素を指定することでレスポンスが拡張されます。

シンプルなクエリ②
query {
  allLifts {
    name
    status
  }
}

返ってくるレスポンスにstatusが追加されました。
FireShot Capture 159 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

複数のデータポイントに対するQuery

以下のように組み合わせることで、複数のデータポイントからの取得クエリを1つにまとめることができます。RESTの場合は別APIに対して2度リクエストを発行する必要がありますが、GraphQLの場合はこのような柔軟性により一度のリクエストで完結します。

複数のクエリ
query liftsAndTrails {
  allLifts {
    name
    status
  }
  allTrails {
    name
    difficulty
  }
}

※長いのでallLiftsの配列は閉じていますが、それぞれのデータを取得することができました。
FireShot Capture 172 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

一致取得

IDを指定して、1件ヒットしたデータをピンポイントで取得することができます。ORマッパーのfindByIdに相当します。

IDを指定して取得
query jazzCatStatus {
  Lift(id: "jazz-cat") {
    id
    name
    status
    night
    elevationGain
  }
}

FireShot Capture 163 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

フィルタ

ステータスを指定することで、取得するデータをフィルタリングすることができます。

フィルタされたデータを取得
query closedLifts {
  allLifts(status: CLOSED) {
    name
    status
  }
}

FireShot Capture 162 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

件数の取得

Liftオブジェクトの件数を取得できます。今回は条件指定が必須のため、CLOSEDステータスの件数をカウントしています。

件数の取得
query liftsAndTrails {
  liftCount(status:CLOSED)
}

FireShot Capture 164 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

エイリアスの指定

別名として名前を指定することもできます。

複数のクエリリクエスト
query liftsAndTrails {
  closed: liftCount(status: CLOSED)
  alists: allLifts {
    c: name
    status
  }
  blists: allTrails {
    name
    difficulty
  }
}

FireShot Capture 165 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

入れ子

オブジェクトを取得する場合は要素が入れ子になります。

入れ子の取得
query liftToAccessTrail {
  Trail(id:"river-run") {
    groomed
    accessedByLifts {
      name
      capacity
    }
  }
}

FireShot Capture 166 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

フラグメント

冗長な繰り返しを避けるために、フラグメントを使用することができます。

以下ではliftInfoとして3つのプロパティをまとめることで、TrailLiftそれぞれで同じ宣言を繰り返す必要がなくなります。

フラグメントの利用
fragment liftInfo on Lift {
  name
  night
  elevationGain
}

query {
  Trail(id: "river-run") {
    name
    difficulty
    accessedByLifts {
      ...liftInfo
    }
  }
  Lift(id: "jazz-cat") {
    ...liftInfo
    trailAccess {
      name
      difficulty
    }
  }
}

FireShot Capture 167 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

Mutations

Mutationを使って更新が可能です。

まずは現状のステータスを確認してみます。allLiftsを使用して変更したい要素のステータスを見てみましょう。

query all {
  allLifts {
    id
    name
    status
  }
}

FireShot Capture 168 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

この一番上に表示されたIDが「astra-express」のステータスが現在OPENなので、CLOSEDに変更してみます。

mutation setStatus {
  setLiftStatus(id: "astra-express" status: CLOSED) {
    id
    name
    status
  }
}

想定どおりCLOSEDに変更できました!
FireShot Capture 169 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

※念の為元のOPENに戻しておきましょう。

mutation setStatus {
  setLiftStatus(id: "astra-express" status: OPEN) {
    id
    name
    status
  }
}

FireShot Capture 170 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

入力値バリデーション

更新する際に、バリデーションを事前に定義することが可能です。$文字列の後に続くものが変数となり、:の後に型と必須チェックを記載しています。

例えば、文字列型で必須にしたい場合は$str:String!、数値型で必須ではない場合は$num:Intとなります。

※今回はスキーマ自体がID/Status共に必須ではありますが、文法の整理として確認していきます。

バリデーション定義
mutation setStatus($id:ID! $status:LiftStatus!) {
  setLiftStatus(id:$id status: $status) {
    id
    name
    status
  }
}

IDを指定して、ステータスをOPENに変更します。

QUERY_VARIABLE
{
  "id": "astra-express",
  "status": "OPEN"
}

FireShot Capture 174 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

Subscription

subscriptionはサーバからリアルタイムの更新情報をプッシュして欲しい時に使用します。queryやmutationと違い、subscriptionはWebSocketで接続された状態を維持してサーバ側の更新を待ちます。

まずは以下を実行して待ち状態にしましょう。

subscription {
  liftStatusChange {
    id
    name
    status
  }
}

別タブで同じURLを開きます。別タブからステータス更新のmutationを投げてみます。

mutation setStatus {
  setLiftStatus(id: "astra-express" status: OPEN) {
    id
    name
    status
  }
}

subscriptionのタブに戻ると、更新されたことをトリガーにデータを取得していることがわかります。下記では2回ステータスをOPENに更新するmutationを投げました。

FireShot Capture 175 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

Introspection

スキーマで定義されているタイプにどんなものがあるかを確認したい場合は、__schemaクエリを投げることで確認できます。

typesの定義確認
query {
  __schema {
    types {
      name
      description
    }
  }
}

FireShot Capture 176 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

特定のtypeについて詳しくみたい場合は、__typeクエリを使用することで確認できます。
以下ではTrailのすべてのフィールドについて確認することが可能です。

query trailType {
  __type(name:"Trail") {
    name
    fields {
      name
      description
      isDeprecated
    }
  }
}

FireShot Capture 177 - Playground - http___snowtooth.moonhighwa_ - http___snowtooth.moonhighway.com_.png

shunp
2018年Amazonが選ぶベストアーキテクチャに金融業界から日本初選出。
https://storygate.info/443502378
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
この記事は以下の記事からリンクされています
granoesteGraphQL for Dartからリンク
sanoyo【JS】GraphQL 基礎からリンク
jintzGraphQLに入門するからリンク
過去の1件を表示する
コメント

例示していただいたコードを見ると、

query {
  ...
}

という形式と

query foo {
  ...
}

という形式があるようですが、違いは何でしょうか?

わかりやすいです、ありがとうございます!

あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした