エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

新規アプリでGraphQLを全面採用した話

この記事は エムスリー Advent Calendar 2019 の17日目の記事です。
こんにちは、エムスリーエンジニアリンググループ マルチデバイスチームの金丸 (@maru333) です。
アナと雪の女王2 は最高でしたね。ただ、劇中の波のシーンが実写か3DCGかを考えだすと気になって夜も眠れません。

さて今日のAdvent Calender では、新アプリを開発する中でGraphQLを試行錯誤しながら色々試した話をしたいと思います。

f:id:kanemaru-m3:20191216220546j:plain
栃木の大谷資料館 巨大地下空間に行った時の写真。ここはPV撮影や映画撮影など様々な事に使われる空間で、今ドラクエをやっているのでドラクエっぽいなと思って上げました (本文とは一切関係ありません)

GraphQLで言うと、弊社主催でGraphQLの勉強会を9月に開催しており、そこからのアップデートを主にご紹介するつもりです。

勉強会の内容はこちら (GraphQL について語る会を開催しました - エムスリーテックブログ)

私の発表資料はこちら (GraphQLのステキポイントと悩ましいポイント - Speaker Deck)

前回からのアップデートは主に以下3点です。

  1. REST API と比べ、1リクエストで複数のクエリを実行できるため認証周りに負担をかけなくて済む!

  2. エラー周りは extensions を使って結局解決した。

  3. セキュリティテストってどうやるんだ…

REST API と比べ、1リクエストで複数のクエリを実行できるため認証周りに負担をかけなくて済む!

ここ最近、動画配信などの期間限定コンテンツが増えたおかげで、認証周りへのアクセスが集中する時間帯が発生することがあります。

今までの REST API のやり方だと、アクセス毎にそのセッションIDが生きているか、どのユーザかの認証を走らせる必要があります。 しかも、アプリから一度に何リクエストも投げなければならないケースも多く、これが認証サーバの負荷を増大させてしまっていました。

もちろん、サーバ側でアカウント情報をキャッシュし、認証サーバへのアクセス自体を軽減するというやり方もありますが、 会員ステータス、ポイント周りなど、そもそもキャッシュすることによって古いデータが表示されてしまうためキャッシュが推奨されないデータもあり、 結局そのデータ周りを取得するためには毎回アクセスせざるを得ないという状況が発生してしまいます。

ここでGraphQLを使うと、アクセス時に最初の一度だけ認証データを取得し、クライアントに返すまで使いまわすことができるので REST API に比べると数分の一のアクセス数に軽減させることができました。ただ、アクセス集中時はやはり認証サーバの応答が遅くなってしまうので キャッシュ可能なデータはサーバ側でもキャッシュさせるなどの工夫も必要になります。

エラー周りは extensions を使って結局解決した。

エラーの処理周りが公式ドキュメントにも触り程度しか書かれておらず、「extensions という何でも入るフィールドに入れてください」 という説明になっています。このあたりの情報を検索してみると、どういう値をクライアントに返すか皆さん色々苦労されているようです。 extensions ではなく、独自の型を定義しスキーマとしてエラー定義を解消しようとする記事もありましたが、現時点ではクライアントの実装が 複雑になってしまうため、あまり上手く解決できませんでした。

今回のプロジェクトでは、結局 extensionsを利用し、code と errorCode の2つを返すようにしています。

override fun getExtensions(): MutableMap<String, Any> = mutableMapOf(
    "code" to httpStatus.value(),
    "errorCode" to errorCode
)

上記コードのように、code には HTTP StatusCode を返すようにしていて、認証失敗時の場合などに活用しています。 また、errorCode はシステムで独自に定義したエラーコードで、この値とメッセージはサーバ側で定義していますが、 Kotlin MPP を用いて Android、iOS でもエラーコードとメッセージのコードを共通化する予定です。(対応中)

code と errorCode の2つがある理由については、一般的な401などのエラーはそのままの値でクライアントで判定した方が、 独自のコードでの判定にするより可読性が高くなりそうという理由でcodeを追加していますが、ここのあたりは再考の余地があるかもしれません。

スキーマを読んだ時にどういうエラーが返ってくるかというのが読めないという課題は残りますが、 上記ルールを共有することでサーバもクライアントもスムーズに解決に向かったので、一つの解ではないかと考えています。

セキュリティテストってどうやるんだ…

リリースも大詰めに入った頃、ある疑問が湧きました。
リリース前になるとセキュリティ的に問題ないか確認するため、セキュリティスキャンを実施しなければいけません。 REST API の場合は有償/無償含めてたくさんのツールが存在し、弊社でも有償のツールを利用してセキュリティスキャンを実施しています。

しかし、GraphQLのセキュリティスキャンは全く実績がありませんでした。セキュリティチームに相談したところ、HTTP通信としてのチェックは可能とのこと。HTTP通信であることは間違いないので、エンドポイントに攻撃を仕掛けてもらい、問題なしとなりました。

これについてはちょっと気になったので、情報を探してみました。色々なブログ等を漁っていく中で、セキュリティに言及しているブログ*1 を見つけ、これによると、以下の3点のテストが挙げられています。

  • Broken Access Controls
  • Insecure Direct Object References
  • SQL/NoSQL Injections

上記いずれも既存のツールではカバーできない範囲でしたので、いくつか追加でシナリオを作成し、実施することで問題なしという判断としています。

GraphQLを使ってみて

GraphQLを使うことでスキーマファーストの開発の流れに上手く乗ることができ、サーバ、クライアント共に幸せになれたのではないか?と思っています。(詳しくは先ほどご紹介した勉強会の内容をご参照ください)

今後は、上記悩んだポイントなど他チームでGraphQLを使っているメンバーとも今まで以上に連携し、より良くしていければと考えています。

We are hiring!

マルチデバイスチーム では、サーバサイドKotlin+GraphQLで一緒に開発に参加してくれる仲間を募集しています。 下記より、お気軽にお問い合わせください!

open.talentio.com

jobs.m3.com