一休.com Developers Blog

一休のエンジニア、デザイナー、ディレクターが情報を発信していきます

新管理画面のAPIにGraphQLを採用した話

一休.com レストランを開発している所澤です。この記事は一休.comアドベントカレンダーの10日目の記事です。

先日、一休.comレストランの管理画面をリニューアルしました。 この記事ではその際にAPIの実装方法として採用したGraphQLについてフロントエンド視点で利点や使い所について述べます。

GraphQLについて以下の記事がわかりやすかったです。

「GraphQL」徹底入門 ─ RESTとの比較、API・フロント双方の実装から学ぶ - エンジニアHub|若手Webエンジニアのキャリアを考える!

短いまとめ

  • 新しくAPIサーバーを書くなら是非GraphQLで! というくらい良かった
  • Apolloのエコシステムに乗り切らなくてもいい。ふつうのRESTfulなAPIサーバーの代わりに、くらいの気軽さでGraphQLを採用してもいい

プロジェクトの概要

今回リニューアルした一休.comレストランの管理画面の概要は以下の通りです。

  • レストラン店舗向けの管理画面
  • 主な用途は在庫の管理と、プラン(コース)や席の管理

f:id:shozawa:20191210085303p:plain

アプリケーションの構成

  • サーバー

    • すでに運用中のアプリケーションに新しいエンドポイントを追加した
    • Python + Flask 製
    • /api でRESTfulなAPIを提供している
    • /graphql 以下にGraphQL形式のAPIを新設
  • フロントエンド

    • TypeScript + Vue.js + Stimulus 製
    • 一部の画面はSPA、大半の画面は jinja2 でレンダリング

サーバーサイドは Graphene-Python と Flask-GraphQL。フロントエンドは特にGraphQL用のライブラリは使用せずに axios を使ってAPIリクエストを行う設計にしました。

GraphQLを利用したAPIサーバーと言うとBFF( Apollo Server など)としてサーバを立てている事例をしばしば目にしますが、今回は既存のAPIサーバーにGraphQL用のエンドポイントを追加しています。また Graphene-Python はコード・ファーストなライブラリなので「まずSchemaファイルを定義してそれをフロントとサーバーで共有して...」といういわゆるスキーマ駆動開発は行っていません。

フロントで vue-apollo を使っていない理由は状態管理に Vuex を採用したためです。vue-apollo とVuexの両方を使う、あるいはローカルの状態管理を Vuex ではなく apollo-client で行うことも考えましたが、今回はVuexのみで状態管理を行い、単にRESTful APIの代わりとしてGraphQLを使うだけの構成としました。

以上のようにあまりGraphQL(や Apollo)のエコシステムに乗っかっているわけではありませんが、それでもGraphQLを採用する利点は十分にあると感じました。

GraphQLのメリット

半年ほどGraphQLを使ったフロントエンド開発に携わってみて感じたGraphQLのメリットは以下の3点です。

  1. 関連リソースを簡単に取れる
  2. 常に最新のAPIドキュメントが手に入る
  3. APIの設計で悩むことが減る

それぞれ詳しくみていきましょう。

1. 関連リソースを簡単に取れる

GraphQLの一番の特徴は複数のリソースを一度のQueryで取得できる点です。

f:id:shozawa:20191210084940p:plain

例えば上記の画面だと

  • プランの情報(プラン名・利用可能人数など)
  • プランの販売状況
  • プランに紐づいている席の情報(席名など)
  • 座席の販売状況
  • カレンダー

などのリソースを一度のリクエスト取得しています。

一般的にドメインモデルが独立していることは少なく、それぞれが1対多あるいは多対多で結びついています。我々のビジネスだと「レストラン」「プラン(コース)」「座席」「在庫」などがそうです。

レストランのページであればレストランに紐づくプランと座席の一覧。

restaurant(id: $restaurantId) {
  plans { ... }
  seats { ... }
}

プランの詳細ページであればプランが属するレストランと、プランに紐づく座席。

plan(id: $planId) {
  restaurant: { .. }
  seats: { ... }
}

というようにGraphQLであればモデルの has many belongs to の関係がそのままQueryで表現できます。

2. 常に最新のAPIドキュメントが手に入る

Graphene(やApolloなどのGraphQLサーバー・ライブラリ)にはGraphiQLというイン・ブラウザIDEが付属しています。GraphQLのエンドポイントをブラウザで開くとエディタが立ち上がり任意のGraphQLが実行できたり、Schemaを確認したりできます。

f:id:shozawa:20191210085040p:plain

サーバーサイドとフロントエンドを分業して開発する場合に特に重要なのは、後者の"Schemaが確認できる"という点です。

ドキュメント生成ツールでAPIドキュメントを用意した場合はドキュメントの更新忘れなどで仕様と実装に齟齬が出てしまうことがありますが、GraphQLであればそのようなことはありません。コード・ファーストのGrapheneではこのSchemaはコードから自動で生成されているので常に実装と同期されています。さらにSchemaにはコメントをつけられるので、Schema定義をそのままAPIドキュメントとして利用できます。

3. APIの設計で悩むことが減る

例えばあるプランを販売中止にするAPIについて考えみましょう。素朴なREST APIだと /plans/:id に対して { status: 'suspended' } をPUTで送ることが考えられます。

「いやいや、リソースの部分更新はPATCHで」

「販売状態をプランのサブリソースだと考えると/plans/:id/on-sale にDELETEではないか」

などというコメントを頂きそうですが、RESTful APIの難しいところはまさにそこです。リソース思考で美しいAPIを設計するのは難しく、また実装者によってインターフェースのゆらぎが出やすいのです。

GraphQLのMutationは"動詞 + 対象"の形式で可能な限り用途を明確に絞って書くのが良い、とされています。今回のケースだと suspendPlan(planId: Int!) とかですね。普段からリソース思考で物事を考えている人は少ないと思うので、こちらのほうが日常生活のマインドモデルと近く適切なMutation名が思いつきやすいはずです。

GraphQLの使いどころは?

今回は(BFFではない)通常のAPIサーバーにGraphQLを導入した事例をご紹介しました。 Apolloなどのエコシステムを抜きにしてGraphQLのことだけを考えると、GraphQLはあくまでHTTPの上に乗った薄いプロトコルに過ぎません。/resourceA/?embed=resourceB,resourceC のようなエンドポイントを生やしたくなったときや、実装と乖離したAPIドキュメントに困ったときなどに気軽に導入を検討してみてください。