なぜGraphQLを採用するべきなのか、あるいはなぜ採用するべきではないのか 2021冬
この記事はGraphQL Advent Calendar 2021の22日目の記事です。
またこれは書籍、出来る100%TypeScript 作って学ぶNext.js + GraphQL + Prismaに掲載していたコラムに加筆修正を行ったものです。
GraphQLは一言でまとめてしまえばDSL(GraphQL query language)による宣言的な記述を介してGraphQLサーバーから柔軟にデータを取得/提供する事が出来る仕組みです。文法は全く異なりますが動作モデルとしてはSQLとRDBの関係に近いかもしれません。なおHTTP上で利用される事がほとんどですが特に決まりがあるわけではありません。
元々はFacebook社(現Meta社)で開発され2012年からfacebook.comで利用されている技術で、その後2015年にはオープンソース化されFacebook以外でも徐々に利用されるようになっていきました。例えばGithub利用者であればそのAPIのv4がGraphQLで提供されている事を知っているかもしれません。
では我々市井の開発者にとって、GraphQLを採用する事には伝統的なREST APIや他の選択肢と比べてどういったメリットやデメリットがあるのでしょうか。
メリット
APIサーバーが提供する型をクライアントサイドで利用できる
GraphQLツールチェーンの中にはAPIサーバーが提供するスキーマ情報等を元にクライアントサイドで利用できる型やクライアントコードを生成出来るものがあり、これによってクライアントサイドへ外界からやってくる型の問題を解決(緩和)する事ができます。
こういったサーバーからの提供に頼らず型の安全性を高めたい場合には、例えばTypeScriptであればio-tsのようなバリデーションライブラリ等によってクライアントサイドで自力解決し、サーバーサイドとクライアントサイドとでスキーマの二重メンテナンスをしていく必要があります。
ただし伝統的なREST APIでもOpenAPI(Swagger) generatorのような仕組みと組み合わせる事で、あるいはgRPC等でも同様の利点を得る事は出来ます。
通信効率が良い
特にモバイル回線を利用するクライアントでは重要になります。
一般的なREST APIの場合、1つのAPIから取得できるリソースは1つのみです。例えば1つのページを作り上げるのに3つのリソースへのアクセスが必要な場合、3回のAPIコールが発生する事になります。APIコールの並列化など実装方法によってある程度の緩和は可能ですが非効率である事には代わり有りません。こういった問題をアンダーフェッチと呼びますが、GraphQLでは1つのクエリーで複数のリソースを取得する文法が規定されているため、クエリー内で宣言的に記述するだけで解決することが出来ます。
また同じリソースへのアクセスであっても、あるページでは全てのフィールドのデータが必要になり、別のあるページでは2つのフィールドのデータだけが必要になる、という状況はよくあるものです。一般的なREST APIの場合、こういった状況では後者のページでも全てのフィールドのデータを取得してしまいがちです。こういった問題をオーバーフェッチと呼びますが、GraphQLではクエリーの中でリソースのどのフィールドを取得するかを指定する文法が規定されているため、クエリー内で宣言的に記述するだけで解決することが出来ます。
ただし伝統的なREST APIでもクライアントとの間にBFF(Backend For Frontend)と呼ばれる層を設ける事で同様の利点を得る事は出来ます。(BFFとしてGraphQLサーバーを利用するケースもあります)またオーバーフェッチに関しては例えばGoogleのAPIデザインガイドラインでは$fieldsパラメータ(gRPCで利用しているProtobufにおいてはFieldMask)という手法も紹介されています。
エコシステムが実用水準まで発達している
本書で利用しているApolloはJavaScriptで実装されたGraphQLサーバーやクライアントを提供していますが、このクライアントはNext.jsのようなWebアプリケーションからだけでなく、React Nativeで作られたネイティブアプリケーション等からも利用する事が出来ます。Apolloプロジェクトはキャッシュ機構なども含まれたオールインワンを志向したライブラリ群ですが、Apollo以外にも様々な実装があるため自身の要件にあったものを選ぶと良いでしょう。
またJavaScriptに限らず様々な言語の実装があるため、サーバーはRubyでAPIモードのRailsと組合せて書き、クライアントはSwiftとKotlinで書くような多言語の混在も可能です。
デメリット
エコシステムが発展途上のため今後まだ大きく変化する可能性がある
例えばGraphQLのスキーマ定義手法ではSDLファーストとコードファーストのように異なるアプローチが並び立っている状況にあります。どちらにも利点はある話ですが将来的にどちらが主流となっていくかは不明です。
またGraphQLクライアントで言えば、Apollo Clientを使うかRelayを使うかあるいは、といった部分も迷いどころです。今後何かがデファクトになりそれ以外は廃れていくのか、それともコンセプトの違いから使い分けられ続けていくのか今後の発展次第の部分があります。
クエリーの自由度の高さ故に負荷予測や対策がし辛い
Securing Your GraphQL API from Malicious Queries
GraphQLと同様に自由度の高いSQLではコントロール下にあるマシンからしかクエリーを受け付けない運用が一般的ですが、インターネットに露出したGraphQLサーバーに対してはブラウザのようなコントロール下にないクライアントからのクエリーも届くため負荷予測やその対策が難しい側面があります。
ただしクエリーコストを分析してリミッターをかけるGithub APIのようなアプローチ、あるいはクエリーの自由度が必要ない用途であればpersistgraphqlのような登録されたクエリーのみを受け付けるアプローチでの対策が可能です。
どちらを選ぶべきなのか
GraphQLのメリットは基本的にはREST APIでもやり方によっては実現できるものです。自身やプロダクトの状況に応じてどちらがより適しているのか考慮をした上で選定すると良いでしょう。
Discussion