新しいことにはウェルカム

技術 | 電子工作 | ガジェット | ゲーム のメモ書き

GraphQLの使い方メモ

使いたい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サーバーによって名前は変わります)
  • Rootperson関数を持ち、関数は指定したidの俳優(Person)を返します
  • PersonidnamebirthYearなどのフィールドを持ちます
  • PersonfilmConnectionフィールドは、PersonFilemsConnection型で、Personの出演した映画(Film)の配列です
  • Filmtitledirectorなどのフィールドを持ちます

クライアント

クライアントからは、ルートとなる型を起点に、型の構造に沿って、取得したいデータを記述して、それを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がいまいち流行らない理由ですかね。

参考記事

関連カテゴリー(コンピューター技術)記事

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com