GraphQLをフワッと学ぶ


はじめに

会社でGraphQLのプロジェクトに参加することになり、それまでRailsくらいしか触ったことなかったこともあり、よくわかっていなかったので、Udemyで下記動画を購入して勉強しました。

【はむ式】フロントエンドエンジニアのためのGraphQL with React 入門

これで学んだことをフワッと説明したいと思います。
この動画ではフロントエンド側での使い方がメインですが、Railsでのバックエンド側の処理も解説したいと思います。

GraphQLとは

GraphQL公式サイトでは「GraphQLはAPI用のクエリ言語およびクエリを実行するためのランタイム」的なことが書いてあります。

要はデータを取得するためのAPIです。
REST APIではURIに応じたデータが返ってきますが、GraphQLも同様にリクエストを送るとリクエストに応じたデータが返ってきます。

ちなみにGraphQLはFacebookが開発しました。

GraphQLのメリット

エンドポイントが一つ

REST APIだと、アクセスURIによって結果が変わってきます。例えばQiitaのAPIでタグの一覧(記事の件数降順)を取得するAPIのURIはhttps://qiita.com/api/v2/tags?page=1&per_page=20&sort=countです。
他のデータを取得するAPIは他のURIなので、取得するデータごとに宛先のURIが変わります。

GraphQLではエンドポイントは一つだけです。
例えばGithubのGraphQL APIのエンドポイントはhttps://api.github.com/graphqlなので、URIの設計をする必要がありません。要はどんなURIにしたら分かりやすいかとかそういうことを考える必要がありません。

必要なデータだけ取得できる

先ほどのQiitaの例で言うとhttps://qiita.com/api/v2/tags?page=1&per_page=20&sort=countにアクセスすると、フォローしてる人の数とかidとか色々取得されますよね。

でももし必要なのがidだけだとしたら他のデータは取得するだけ無駄となってしまいます。
この程度でしたら大した差はないかもしれませんが、膨大なデータの一部だけしかいらない場合、パフォーマンスに影響が出ます。

実装がそのまま仕様書になる

REST APIの場合はどのURIでどのようなデータが返ってくるかのドキュメントを作成する必要があります。
そのためSwaggerなどを使い、ドキュメントを作成(サンプル)するのですが、GraphQLの場合schema主導で開発ができ、GraphiQLなどを使用するとスキーマを見ながら開発が進められる(下記画像)ので、特にドキュメントの作成は不要になりますし、実装がそのままドキュメントになるので、ドキュメントと実装の齟齬がなくなります。

もちろん外部に出すAPIの場合で、追加の説明が必要な場合はそれ用のドキュメントは必要になるかもしれませんが。

フロントクエリの書き方

これは非常にシンプルでクエリ名と欲しいデータを記述するだけです。
GraphiQLのデモで試してみます。

まずは国の名前一覧が欲しい場合、

やっぱり、首都も欲しい。

はい、素敵。
こんな感じで好きなものを取れます。
もちろん引数を指定して特定の国だけも取れます。

サーバー側実装方法(Rails)

動画にはなかったのですが、バックエンド側の実装ももちろん必要なので簡単に解説します。
graphql-rubyをインストールします。

設定はドキュメントを見ていただくとして、ここではPostモデル(titleとcontentカラムを持っている)を作成し、idを渡すと当該ポストを返すクエリを実装します。

まずは型を定義します。

module Types
  class PostType < Types::BaseObject
    description "A post"
    field :id, ID, null: false
    field :title, String, null: false
    field :content, String, null: false
  end
end

これでPostの型ができました。
次にクライアントに返すクエリを作成します。

module Queries
  module Post
    class Post < BaseQuery
      description 'post'
      type Types::PostType, null: true # 返り値の型
      argument :id, Int, required: false # 引数

      def resolve(:id)
        ::Post.find(id) # Postモデル
      end
    end
  end
end

これでidを引数にクライアント側でクエリを発行すると、Types::PostTypeで定義した型の返り値を取得できます。

なので、クライアントで

query {
 post(id:123) {
   id
   title
 }
}

とクエリを発行すればidが123のポストのidとtitleが取得できます。contentも欲しかったらクエリに追加するだけです。簡単。

まとめ

最初よく分からなかったですが、慣れてしまえば分かりやすいですし、フロントとバックエンドの開発チームが違う場合、ドキュメントを作らなくてもフロント側に何も説明することなくバックエンド側の実装だけに集中できるのでとても良いと思います。