GraphQL でカクテル検索サービスを実装してみた


はじめに

こんにちは。自分は普段 React でフロントを実装しています。最近 GraphQL に興味があって、REST 以外の選択肢を知っていると技術選定の幅が広がると思い、GraphQL で何かサービスを実装してみました。最後に感想を述べていますので、GraphQL への理解を深める参考になれば幸いです。以下に実際に動作する成果物を載せておきます。

サンプルとコード

demo: https://liquor-react.netlify.com/
client source: https://github.com/yoneda/liquor-react-client
server source: https://github.com/yoneda/liquor-graphql-server

  • 2019年12月15日 追記
    • GraphQL サーバーのデプロイ先をGlitch からheroku に移行して表示速度を改善しました。

全体の構成

フリーで使えるデータを探していたところ、RapidAPI というサービスが見つかりました。そこで酒やカクテルを検索できる Cocktail DB API が見つかったので今回はこちらを使用しました。クライアントは React、サーバーは GraphQL で実装しました。GraphQL は、DB だけでなく、他の REST API だったり、または他の GraphQL も接続できるということでした。GraphQLはDBと接続されることが多いと思いますが、今回はREST形式のRapidAPI と接続しました。GraphQL は BFF のような働きをします。図にすると以下のような感じです。

使用技術

今回使った技術は以下の通りです。
クライアント: react, apollo-client, react-scripts, reach-router
サーバー: express, express-apollo-server, superagent, nodemon

サーバーサイド

サーバーサイドは Express + ApolloServer で実装しました。ApolloServer ではスキーマを定義してそれに対応するリゾルバを実装していきます。RapidAPI から返されたJSON を見ながらスキーマを定義します。カクテルのリストを返すqueryは以下のようにしました。

  enum DrinkCategory{
    COCKTAIL
    COFFEE
    BEER
    OTHER
  }
  type Query{
    allDrinks(category: DrinkCategory): [Drink!]!
  }
  type Drink{
    id: ID!
    name: String!
    url: String!
    category: DrinkCategory
  }

クライアント

クライアントは React + ApolloCLient で実装しました。React で GraphQL のクエリを発行するのはシンプルでした。コンポーネントのルートで GraphQL の URI を設定する、useQuery の Hooks でデータをフェッチする、この2つだけでサーバーと接続可能です。

実装した感想

Apollo Client の強力なキャッシュ機能が便利

Apollo Clientは、サーバーからのフェッチ結果をどこでもローカルにキャッシュします。Aコンポーネントでブログ記事を取得した後、Bコンポーネントから同じものをフェッチしようとしたときは、再度サーバーにリクエストを送らず、ApolloClinet がキャッシュしているデータにアクセスするので高速に表示されます。こういった実装はこれまでは Redux によって行われていました(AコンポーネントとBコンポーネントがアクセス可能な Redux の値を用意する。Aコンポーネントでブログ記事を取得した結果を Redux に入れておいて、Bコンポーネントがフェッチしようとしたときは代わりにRedux の値にアクセスするようにする)。Redux によってAction や Reducer を書いて、ほぼ自前で実装してたキャッシュの機能が、ApolloClient では最初から用意されています。コードを書く量が減ると思います。

バリデーションの手間が減る

サーバーサイドを Node.js で実装していて、バリデーションしたいときは別途ミドルウェアを組み込む必要がありました。ここでいうバリデーションとは、Request のパラメータ値の型をチェックしたり、その値が必須かどうかなどをチェックするような工程です。GraphQL でクライアント-サーバー間で型が保証されているというのは、開発者を安心させます。

GraphQL はリクエスト回数が少なくてすむ

REST と GraphQL の特徴を比較したときに、どちらが効率的にデータを取得できるかというと、自分は GraphQL の方が優れていると思います。特に、あるデータとその関連データを取得するような場面では、GraphQL の良さを発揮できるようでしょう。例えば、ブログ記事とその記事についたコメントを取得するAPIを実装してみるとします。

GET /articles/:id
GET /articles/:id/comments

REST では、上記のようにと2回リクエストを送るように実装するのが自然です。(もちろん、ブログ記事とそのコメントを同時に取得するような実装も可能です。1つのリソースに GET / POST / PUT / DELETE を割り当てて設計していく REST の思想に忠実に従うと、上記のような設計のほうが自然ではないかと思います。)

query{
  article(id: $id){
    title
    body
    postedComments{
      comment
      user
    }
  }
}

GraphQLでは、1回のリクエストで取得できます。さらに、必要なフィールドだけ指定して取得できます。無駄なフィールドは取得せず効率的です。GraphQLはデータを効率的に取得できるような設計になっていると感じました。

次回(予定)

今回はqueryだけだったので、次回はmutationやユーザ認証まで実装したいと思ってます。実装でき次第、この記事に追記するか、新しい記事として投稿する予定です!