使いたいAPIがGraphQLで書かれていました。今回GraphQLを使うのが初めてだったので、クライアント側からのGraphQLの使い方について、簡単にメモしておこうと思います。
GraphQLとは?
GraphQLとは、Facebookが開発したAPIの仕様です。
REST APIと比較して、APIの呼び出し回数や、データ受信量が少なくなるといったメリットがあります。
例えば、ユーザー名と、そのユーザーのフレンド名一覧を取得する場合、REST APIだと、下記の事情により、データ取得に時間がかかります。
- ユーザー情報取得とフレンド一覧取得が、別々のエンドポイントになっていた場合、2度のAPI呼び出しが発生する
- ユーザー名だけしか使わない場合でも、メールなどの他の情報も取得されてしまう
これをGraphQLで行うと、APIに下記のリクエストを1度送信するだけで、必要最小限のデータが取得できます。
query { user(id:123){ name friends{ name } } }
実際に試してみる
公式ドキュメントのチュートリアルでは、スター・ウォーズのデータで説明されているので、同様にスター・ウォーズAPIのGraphQLラッパーで試しました。
ただし、公式ドキュメントの例と、スキーマが違うので注意が必要です。
どういったスキーマ(データ構造)かは、右上の「Docs」から調べることができます。
GraphQLの基本的な考え方
サーバー
GraphQLは、まずサーバーで型が定義されていています。
そして、クライアントからは、取得したいデータの型とフィールドを指定して、そのデータを取得します。
例えば、スター・ウォーズAPIのサーバーでは、下記のような型が定義されています。(説明しやすいよう部分的に修正しています)
type Root { person(id: ID): Person } type Person { id: ID name: String birthYear: String gender: String filmConnection: PersonFilmsConnection } type PersonFilmsConnection { films: [Film] } type Film { title: String director: String producers: [String] }
解説
Root
はルートとなる型で、サーバーへの問い合わせはここから始まります(型名はサーバー側で任意に定義できるので、アクセスするGraphQLサーバーによって名前は変わります)Root
はperson
関数を持ち、関数は指定したidの俳優(Person
)を返しますPerson
はid
、name
、birthYear
などのフィールドを持ちますPerson
のfilmConnection
フィールドは、PersonFilemsConnection
型で、Person
の出演した映画(Film
)の配列ですFilm
はtitle
、director
などのフィールドを持ちます
クライアント
クライアントからは、ルートとなる型を起点に、型の構造に沿って、取得したいデータを記述して、それをGraphQLサーバーにリクエストします。
例えば、Luke Skywalker
のidは「cGVvcGxlOjE=
」なのですが、Luke Skywalker
の名前と性別を取得するのは下記クエリになります。
クエリ
query { person(id: "cGVvcGxlOjE="){ name gender } }
結果
{ "data": { "person": { "name": "Luke Skywalker", "gender": "male" } } }
解説
query
以下に、ルートとなる型を起点として、取得したいデータを記述します- 関数の引数は「<引数名>:<値>」で渡します
- 結果は
data
以下に、クエリで指定した構造で返されます
配列要素も取得できます。その際、配列の指定は[]
ではなく、要素と同じ{}
でアクセスします。
例えば、Luke Skywalker
の出演映画名一覧を取得するには、下記クエリになります。
クエリ
query { person(id: "cGVvcGxlOjE="){ filmConnection{ films{ title } } } }
結果
{ "data": { "person": { "filmConnection": { "films": [ { "title": "A New Hope" }, { "title": "The Empire Strikes Back" }, { "title": "Return of the Jedi" }, { "title": "Revenge of the Sith" } ] } } } }
以上が基本になります。
サーバーは型で、クライアントからはその型を辿ると考えれば、イメージしやすいかと思います。
エイリアス
フィールド・関数に別名を付けることができます。
クエリ
query { hero: person(id: "cGVvcGxlOjE="){ name } heroin: person(id: "cGVvcGxlOjU="){ name } }
結果
{ "data": { "hero": { "name": "Luke Skywalker" }, "heroin": { "name": "Leia Organa" } } }
変数
変数を定義して、クエリに値を変数で渡すことができます。
これにより、クエリのテンプレート化ができて便利です。
クエリ
query ($personID: ID="cGVvcGxlOjE="){ person(id: $personID){ name } }
変数
{ "personID": "cGVvcGxlOjU=" }
結果
{ "data": { "person": { "name": "Leia Organa" } } }
- 変数の値の展開は、サーバー側で行ってくれます
- デフォルト値を設定する場合は「$<変数名>: <型>=<デフォルト値>」で行います
- 型名は、サーバー側で設定されたものに合わせます
データの追加・更新
データの追加・更新はquery
ではなく、別途mutation
というルートが用意されていて、そこで行います。
クエリ
mutation { createUser(name: "test test"){ id name } }
結果
{ "data": { "createUser": { "id": 123, "name": "test test" } } }
呼び出し
実際の呼び出しは、エンドポイントにデータをセットしたJSONをPOSTすることにより行います。
JSONは、query
にクエリ文字列、variables
に変数のJSONをセットします。
例)JavaScript
import axios from 'axios'; (async () => { const query = ` query ($personID: ID){ person(id: $personID){ name } }`; const variables = { personID: "cGVvcGxlOjE=" }; const json = { query, variables }; const res = await axios.request({ method: 'POST', url: 'https://swapi.apis.guru', headers: { 'Content-Type': 'application/json' }, data: json }); console.log(res.data); })();
その他
認証
認証方法については、GraphQLでは決められておらず、サーバーの実装に任されています。
ページング
ページングについては、GraphQLでは決められておらず、サーバーの実装に任されています。
例えばスター・ウォーズAPIで、俳優一覧を取得する場合、結果にページング情報も出力されるので、それを使ってページ指定をしてクエリをする仕様になっています。
クエリ
query { allPeople(first: 2, after:"YXJyYXljb25uZWN0aW9uOjE="){ pageInfo{ endCursor } people{ name } } }
結果
{ "data": { "allPeople": { "pageInfo": { "endCursor": "YXJyYXljb25uZWN0aW9uOjM=" }, "people": [ { "name": "R2-D2" }, { "name": "Darth Vader" } ] } } }
感想など
SPAなどのアプリでサーバーからデータを取ってくる時、GraphQLだと呼び出し1回で済み、データ転送量も少なくなるので、ユーザーレスポンスが早くなります。
正にFacebookが必要に迫られて開発したものって感じですね。
使う側は楽でいいんですが、サーバー側はライブラリが用意されているものの、実装は面倒です。
UX重視のアプリで使う分には導入メリットは大きいですが、レスポンス速度を気にしない情報提供APIだと、実装が面倒な割にはメリットはないかなと思いました。
そのあたりが、GraphQLがいまいち流行らない理由ですかね。
参考記事
- https://qiita.com/NagaokaKenichi/items/f83148f4903b17d1d2f0
- https://qiita.com/bananaumai/items/3eb77a67102f53e8a1ad
- https://gift-tech.co.jp/articles/start-graphql/
- http://apis.guru/graphql-apis/