なぜGraphQLを使うべきではないのか

2020/11/13 06:32

Jens Neuse    
GraphQLの柔軟性、RESTのシンプルさとハイパフォーマンスを組み合わせたバックエンドサービス「WunderGraph」の創業者
この記事は、著者の許可を得て配信しています。
https://wundergraph.com/blog/why_not_use_graphql

私はGraphQLで世界が変わると思います。GraphQLを使って世界中のどんなシステムでもクエリができる未来が来るでしょう。私はその未来を作っているのです。では、なぜ私はGraphQLを使うことに反対なのでしょうか?私が個人的に嫌なのは、コミュニティがGraphQLの利点を宣伝し続けているときに、それが非常に一般的なもので、実際にはGraphQLとは全く関係のないものだという点です。もっと普及させたいのであれば、正直に言って、楽観的過ぎる見方を改めるべきだと思います。この投稿は、Kyle Schrade氏による(https://www.apollographql.com/blog/why-use-graphql/) の「なぜGraphQLを使うべきか」への回答ですが、直接的な批判を意図したものではありません。この記事は、私がコミュニティでよく耳にする意見を代弁したものであり、仕事をする上での優れたベースになっているものです。記事全体を読めば、時間がかかるかもしれませんが、私がKyle氏の記事を「なぜApolloを使うべきか」という名前にすべきだと思う理由が完全に理解できるでしょう。

もしまだKyle氏の記事を読んでいないのであれば、まず最初に読んでみてください: https://www.apollographql.com/blog/why-use-graphql/

RESTの欠点

Kyle氏は、REST API には一連の欠点があり、そのすべてを GraphQLがどのように解決しているかを述べています。複数のリソースWaterfallネットワークに対するオーバーフェッチしているマルチプルリクエストはネストされたデータにリクエストします。それぞれのクライアントはそれぞれのサービスの場所を把握する必要があります。

最初の3つの問題は、特定のユーザーインターフェースのためのファサード(建物で言うと正面から見た外観)として別のREST APIを書くことで解決できます。Next.JSを例に挙げてみましょう。Nextでは、非常に軽量な構文で API を定義することができます。クライアントから複数のリクエストをする代わりに、それらの呼び出しをAPIにラップしてサーバーサイドで行うことができます。オーバーフェッチやアンダーフェッチもこのアプローチで解決できます。説明されているパターンは、「backend for frontend (BFF)」 と呼ばれています。Next.JSのようなフルスタックフレームワークに限定されることはなく、モバイルアプリでもBFFを構築することができます。

BFFパターンでは、クライアント自身が各サービスの位置を把握する必要はありません。しかし、BFFを実装する開発者は、サービスのランドスケープを把握する必要があります。可能であれば、すべてのサービスのOpen API仕様書が開発者ポータルにきれいに表示されているようになるのが理想です。そうであれば、BFFを書くのは簡単なはずなのです。

GraphQLの場合、やはりリゾルバを実装する開発者が必要になってきます。リゾルバを実装するのはBFFを構築するのと大体同じ作業で、そのロジックはとても似ています。ただ似ていても違いがあります。その違いはどういう点でしょうか。

実装に関して言えば、BFFの方が簡単です。それは、利用可能なツールが多いからです。例えば、Next.JSのようなフレームワークをswrフック(stale while revalidate)と組み合わせて使えば、Etagsを使った自動キャッシュとキャッシュ無効化がすぐにできます。これにより、サーバーとクライアントの間で送信されるデータ量を減らすことができます。クエリのペイロードを送信していないため、それは GraphQLよりもさらに少ないデータであり、応答がまだ有効な場合、サーバは 304 (Not Modified) で応答します。さらに、Apolloのようなヘビー級のクライアントを使う必要はありません。Vercel のライブラリ swr は小さくてとても便利です。ページネーションやフックに対応しており、非常に効率的に前後に移動するのを助けてくれます。

GraphQLには永続的なクエリがありますが、これを実装するためにさらなるオーバーヘッドが発生します。デフォルトでクエリを永続化してくれるRelayのようなクライアントを使わない場合は、自分でやるか、サードパーティのライブラリを使って実装する必要があります。Next.JSなどを使ったBFFのアプローチと比べると、フロントエンドで同じ結果を期待するには、より複雑な作業が必要になります。どのようにしてEtagsをGraphQLで実装するのでしょうか?何も変更されていない場合は、どのようにあなたのGraphQLサーバーは304ステータスコードを返すのでしょうか?まず、すべてのクエリをGETリクエストにする必要はないのでしょうか?もしそうなら、あなたのGraphQLクライアントとサーバは、すぐに対応してくれますか?

ユーザーエクスペリエンスと開発のしやすさという点では、BFFの方が明らかに優れています。クライアントとサーバ間のデータ転送が少なくて済むのです。実装が簡単ですし、クライアントが小さく、可動部分が少ないのです。

しかし、そこに落とし穴があります。個々のフロントエンドごとに BFF を構築しなければなりません。多くのフロントエンドを持っている場合、これは大変な作業になります。すべての BFF を管理しなければなりませんし、それらを操作しなければならないからです。またセキュアに関しても考慮が必要です。

トレードオフをすることなく、両方のメリットを得ることができればいいと思いませんか?それを実現してくれるのがまさにWunderGraphです。GraphQLを使ってBFFsを構築するフレームワークです。

バージョン管理されたAPIはもう必要ない

次のパラグラフでは、Kyle はバージョンAPI に関連する問題について述べています。彼の言うことはごもっともで、APIのバージョンが多すぎると追跡するのが非常に難しくなります。そして、GraphQLでその問題は解決できるとしています。GraphQLにはグラフのバージョンは1つしかなく、Apolloの有料機能であるスキーマレジストリで変更を追跡することができるからです。そのため、バージョニングには問題は全くないと彼は述べています。

しかし、同じ結論に達するのには問題があると私は思っています。GraphQLスキーマがネイティブにバージョニングに対応していないからといって、問題がなくなるわけではありません。REST API のバージョン管理をしなくても、同じ効果が得られます。実際、多くの専門家は、バージョンAPIを導入する必要がないのであれば、常にバージョンを導入しないようにすべきだと言っています。そうは言っても、GraphQLスキーマの2つのバージョンを実行しない理由は何なのでしょうか?そうするのがいい判断だとは思えませんが、技術的には可能です。

もし、REST APIのバージョンが多すぎることが組織内で問題になっているのであれば、GraphQLのような新しいツールを問題にする前に、まずその組織に問題はないか見返すべきかもしれません。バージョンが多すぎる理由は何でしょうか?もしかしたら、プロセスを変更したり、新しいチーム構造を作ったりすることで解決できるかもしれない。GraphQLはバージョン管理の問題を解決するためのものではありません。むしろ状況を悪化させるものだと思います。

モバイルアプリに対応している必要はあるのでしょうか?ネイティブアプリの出荷には時間がかかることは知っておくべきです。App storeからの承認を待たなければならず、多くのユーザーが新しいバージョンをインストールしない(またはすぐにはしない)ことが予想されます。このシナリオで、クライアントを壊さずに画期的な変更を加えようとする場合はどうすればいいのでしょうか?それは不可能です。クライアントを壊さないようにこの変更を導入しなければいけないんです。Facebookにどのようにしてクライアントを壊さないようにできたのか聞いてみたいものです。

GraphQLの場合、スキーマを進化させるということは、古いフィールドを否定し、新しいフィールドを追加することを意味します。新しいクライアントは新しいフィールドを使用し、古いフィールドを使用するクライアントの数がどんどん減っていくといいですよね。可能なら、ある時点でユーザーに新しいバージョンをダウンロードさせるシステムがあるといいでしょう。そうでなければ、非推奨のフィールドに無期限に対応することを余儀なくされるかもしれません。そのような場合、GraphQLの非推奨モデルは何の役にも立ちません。

RESTを使えば、新しいエンドポイントを作成したり、既存のエンドポイントの別バージョンを作成したりすることができます。問題は同じですが、解決策が少し違うだけです。

はっきりさせておきたいのは、もしクライアントをコントロールできないのであれば、何らかのバージョニングが必要になるということです。もしあなたが持っているのが単一のウェブアプリケーションだけならば、この機能は必要ないでしょう。ただ、繰り返しになりますがGraphQLはちょっとやり過ぎでいるかもしれません。

より小さなペイロード

この段落で、Kyle氏はRESTful APIは部分的なレスポンスを許可しないと述べています。

これは本当に間違いです。以下に例を示します。

GET /users?fields=results(gender,name)

Kyle氏は実際のところどういう意味でそう書いているのでしょう?彼が部分的な応答を認識しているのは間違いないと思います。彼が言おうとしていることは、誰かが部分応答を実装する必要があるということだと思います。実際には、リソースからサブフィールドを選択しているので、 GraphQL にはとても馴染みがあるように見えます。GraphQLでは、この機能がすぐに使えるようになっています。

一方、BFFのアプローチでは、この機能は必要ありません。必要なデータを正確に返すだけです。繰り返しになりますが、Next.JSのようなフルスタックフレームワークを使えば、実装がよりシンプルになり、キャッシュがより簡単になり、Etagベースのキャッシュ無効化を無料で行うことができます。

ここまでの要点をまとめると、GraphQLは必要なデータを正確に提供してくれるということです。部分的なレスポンスでも同じ結果を得ることができます。BFFsには、実装とメンテナンスに対する追加コストがかかりますが、より優れたUXとDXがあります。

厳密に型付けされたインターフェイス

この部分では、Kyle氏はREST APIが厳密に型付けされていないという問題を取り上げています。彼は、post配列なのか何か別のものを取得するのかがわからないAPIの問題や、クエリパラメータがどのように状況を複雑にしているのかについて話しています。また、GraphQLは厳格な型システムのため、このような問題はないと述べています。

私は Kyle氏 がそれを組織的な問題であり、組織的な解決策の必要性だと捉えていると考えています。

Open API Specifications (OAS) のようなものを公開せずに、開発者が REST API をデプロイできるようにすると、彼が説明しているような問題が発生します。OASを使えば、すべてのリソースを非常に簡単に記述することができます。OAS は OAuth2 フローやエンドポイントごとに必要なスコープを記述することもできます。さらに、GraphQLにはない機能である、クエリパラメータの正確な型とバリデーションルールを記述することができます。

GraphQLには、認証、承認、入力バリデーションを記述する方法がありません。GraphQLでそれができないのは、Facebookの発明者たちがこの問題を別のレイヤーで解決していたからなのです。GraphQLにこれらの機能を追加する必要はありませんでした。スキーマにカスタムディレクティブを追加することでOASと同じような結果を得ることができますが、これは自分でメンテナンスしなければならないカスタム実装になります。

OASはAPIのレスポンスが仕様に準拠していることを保証しないと思っている人もいるかもしれませんが、まさにその通りです。しかし、どのようにしてGraphQLスキーマは何かを保証しているのでしょうか?

GraphQLイントロスペクションとは、特定のGraphQLクエリをサーバーに送信して、GraphQLスキーマに関する情報を取得することです。GraphQLサーバーは、どのような型で応答するかは自由です。クエリを送信すると、サーバーはイントロスペクションのレスポンスからGraphQLスキーマに準拠していないレスポンスで答えることができます。Apollo Federationを例に挙げてみましょう。スキーマをスキーマレジストリにアップロードした後、誤って間違ったバージョンの GraphQL サーバーをデプロイしてしまったとします。フィールドの型を変更すると、クライアントは混乱するかもしれません。

GraphQLの型の安全性に関しては、イントロスペクションのクエリレスポンスでアドバタイズされた通りのビヘイビアをするGraphQLサーバーを信頼するということを意味しています。なぜOpen API仕様を同じように信頼できないのでしょうか?私はできると思います。もし信頼しないのであれば、技術的な問題ではなく、人間に問題があります。

クライアントのパフォーマンス向上

次の段落は短いのですが、GraphQLがいかにクライアントのパフォーマンスを向上させ、ネットワークのラウンドトリップを減らすかについてです。

BFFがどれだけ強力か、また「stale while revalidateパターン」からのメリットがどれだけあるかは、ヘビー級のGraphQLクライアントと比較してしっかりと説明したと思います。

つまり、GraphQLは確かにリクエスト数を減らし、全体のデータ転送量を減らすことができます。しかし、フロントエンドにGraphQLクライアントを追加するコストに関しては十分に考慮する必要があると思います。

GraphQLと比較してみましょう。基本的には、SDLが先かコードが先かの2つのアプローチがあります。いずれにしても、最終的にはすべての型とフィールドを記述したGraphQLスキーマが作成され、コメントを付けることができます。

では、何が違うのでしょうか。OASは設定に多くのオーバーヘッドを伴います。一方、OASには、ユースケースの例、認証と承認、入力のバリデーションルールなどを文書化したサポートが組み込まれています。

GraphiQL自体には、複数のGraphQLスキーマの概念がないことを覚えておいてください。複数の GraphQL (マイクロ) サービスを持つ場合は、REST API の開発者ポータルに似たスキーマレジストリなどの専用コンポーネントを実行するか、購入する必要があります。

APIのドキュメント化とナビゲートに費やす時間の短縮

次は、OASのようなツールがRESTful API開発にどのように使われているかという、マイクロサービス環境で複数のOASを維持する際の課題についてです。Kyle氏は、単一のGraphQLスキーマと、複数のgitリポジトリに分散したSwaggerファイルを比較しています。

単一の GraphQL スキーマをナビゲートすることは、git リポジトリに散らばっている複数の OAS ファイルを見るよりもはるかにシンプルであることは明らかだと思います。しかし、公平な観点から言わせてもらうと、同じ条件において比較する必要があります。開発者の生産性を向上させたいのであれば、OAS ファイルを git リポジトリに保存して、それで今日の仕事はおしまいということはありませんよね。開発者ポータルを運営して、APIを検索したり、API間を移動したりもします。

OAS は JSON-Schema に依存しています。つまり、別のドキュメントからオブジェクトタイプを参照することができます。必要に応じて、OASを複数のファイルに分割し、互いに参照し合うことができます。また、複数のOASファイルを1つのOASドキュメントにまとめるツールもあります。このドキュメントを使用して、開発者ポータルにフィードすることで、すべての API を全体として検索することができます。ただし、これらすべを設定するには追加で費用がかかることを忘れないでください。開発者ポータルを運営するか、購入する必要があります。すべてのAPIを記述する必要がありますが、少なくとも最初のうちは負担になります。

一つ付け加えると、オブジェクトやクラスを定義するなど、好きなプログラミング言語でスキーマを記述できるフレームワークはたくさんあります。そうすれば、有名なエンドポイントで提供される自動生成された Open API 仕様を取得することができます。

余分な段落を設けたいと思ったことの一つは、APIのユースケースです。OASやGraphQLスキーマを持っているからといって、APIがしっかりとドキュメント化されているとは限りません。APIユーザーはAPIを使って何ができるのか?どのように使うことができるのでしょうか?良いユースケースは何か?悪いユースケースとは?どこに助けを求めればいいのか?ユーザーを認証するには?API キーは必要なのか?API の消費者が API を使用するのに役立つ方法で API を文書化することは、型やフィールドに説明を追加するよりもはるかに多くの作業です。OASではペイロードの例を追加して記述することができます。GraphQLにはこの機能がありません。肯定的な例としてStripeを見てみましょう。それはSwaggerやGraphQL Playgroundで実現できることをはるかに超えていました。

一方、GitHubの公開されているGraphQL APIを見てみると、Queryの例は一つも見つかりません。例えば、あるリポジトリとそのすべての問題に関する情報を得たいとしましょう。その場合、GraphiQLを開いて検索しなければなりません。しかし、GraphiQL の検索機能はあまり役に立ちません。誰かがAPIをどのように使うかについてのクエリやユースケースの例を書く必要があります。そうしないと、検索を始めるのは本当に難しいです。

コミュニティは「GraphQLはself-documentingしている」と言い続けていますが、この機能だけでは有用なAPIにはなりません。OASはユースケースを追加するためのツールを提供してくれますが、それを書かなければなりません。どのようなツールや言語を選んだとしても、他の人に役立つAPIを作るにはそれなりの努力が必要なのです。

レガシーアプリのサポート

この段落では、モバイルアプリ用のREST APIの古いバージョンを維持するのは苦痛だとKyle氏は言っています。ただ単一の GraphQL サーバーを使用しているだけなので、こういった問題は起きないとKyle氏は結論づけています。

申し訳ありませんが、またしても全く違う結論になってしまいます。バージョニングを禁止するルールにしておけば、新しいエンドポイントを追加したり、既存のエンドポイントの実装をスワップしたりすることができます。この場合、GraphQLとRESTの違いはありません。レガシーアプリのサポートは、RESTとGraphQLのAPIの両方で課題となります。クライアントとサーバーのコントラクトを壊さない方法を見つける必要があります。サーバーがRESTをエクスポーズしていても、GraphQLをエクスポーズしていても、問題は同じです。

エラー処理の改善

エラー処理について、Kyle氏は、部分的なデータで応答する単一の GraphQL クエリに対して、クライアントが 3 回の REST API 呼び出しを行わなければならないというシナリオを説明しています。

GraphQLでは、部分データを解決するロジックはサーバーにあります。クライアントは、部分的な応答に適切に反応するための追加のロジックを持つ必要があります。

RESTでは、部分データを取得するロジックはクライアントにあるか、BFFにあるかのどちらかになります。いずれにしても、ロジックは GraphQL とほぼ同じですが、別の場所にあるだけです。明らかに、REST API のユースケースでは、部分的なレスポンスを処理するためのロジックがクライアントに必要です。このロジックは GraphQL の場合とほぼ同じです。

何かが失敗した理由に関してですが、RESTレスポンスで特定の情報を返すことを妨げるものは何もありません。OASはユニオン型を許可しているので、部分的なレスポンスについての豊富な情報をクライアントに自由に与えることができます。これは、
Sascha Solomon氏(https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc)が説明しているレスポンスユニオンの概念に似ています。

GraphQLは本当にエラー処理が優れているのでしょうか?OAS と GraphQL のどちらも、非常にユーザーがやりやすい方法でエラーを処理するために優れたツールを提供していると思います。これらのツールをうまく利用するかどうかは開発者次第です。

結論

Kyle氏は、パフォーマンス、ペイロードサイズ、開発者の時間、ビルドインドキュメントの点で優れているから、GraphQLはAPIの未来だ、と記事全体を締めくくっています。

GraphQLがAPIの未来であることには同意しますが、その理由は違います。

より優れたパフォーマンスとペイロードサイズの小ささは、GraphQLに特有の機能ではありません。他のツールを使ってより良い結果を得ることができますし、Relayを使って永続的なクエリを取得するなど、GraphQLを拡張する必要があります。GraphQLのドキュメントを最大限活用するためには、スキーマに記述を追加するだけでなく、それ以上のことをしなければいけないのです。

気持ちを落ち着かせた後は、事実を見なければなりません。世間に「GraphQLはすばらしいものだ」と信じ込ませてはいけないのだ。実際はそうでもないと点もあるからです。GraphQLを使うメリットはたくさんありますが、ユースケースによってはその恩恵を受けられないこともあります。使うケースによっては、その恩恵を受けられない可能性もある。GraphQLを過信するのはあまりおすすめではありません。

取り組み方としては、まず問題に目を向け、問題を解決するための可能性のあるツールを特徴的に比較することです。もしあなたの組織がREST APIの実装に失敗した場合、GraphQLはこの問題をどのように解決するのでしょうか?もしかしたら、組織内の何かが変わらなければならないのでしょうか?一方で、それが組織の問題ではなく、自分のユースケースにはRESTが良い代替手段ではないと確実に確信しているのであれば、きっとGraphQLの開発者エクスペリエンスを気に入ると思います。

私自身の考える「なぜGraphQLなのか」

GraphQL自体はほとんど役に立ちません。GraphQLを強力にしているのはツールです。それはコミュニティであり、私たちなのです!Apollo、Hasura、The Guild、FaunaDB、Dgraph、GraphCMS、そしてWunderGraphのような会社がGraphQLをパワフルなものにしているのです。GraphiQL、様々なGraphQLクライアント、Schema Stitching、Federation。このようなツールのエコシステムがあるからこそ、GraphQLは成長するのです。

ツールやサービスが増えれば、エコシステムが強化されます。エコシステムが強化されれば、より多くの企業が GraphQL のエコシステムにサービスやツールを追加することになり、非常に良い循環が生まれます。

この点では、GraphQLはKubernetesと非常によく似ています。コンテナランタイムであるDockerだけでは不十分でした。複雑なSyscallをシンプルで使いやすいAPIでラップすることが可能でしたが、しっかりとしたエコシステムを作るためには、十分に表現力があり、非常に簡単に拡張できるスケジューラが必要だったのです。

GraphQLは、Dockerと同じようにシンプルにAPIを定義し、消費するための言語を提供してくれます。これまでにJavascriptやJSONを数行見たことがある人なら、GraphQLには馴染みがあるように感じるはずです。ただ、Dockerと似てはいますが、言語自体はそれほど強力ではありません。協力なのは、拡張性とその周辺のツールなのです。

Facebookがオープンソースで提供している言語が成功したわけではありません。成功しているのはRelayのようなツールです。残念ながら、内部で使われていたツールの多くは、一般に公開されることはありませんでした。コミュニティはこれに追いつく必要がありましたが、私たちはすでにかなりのところまで来ていると思います。

私の個人的な結論

Kyle氏が「なぜGraphQLなのか」と質問してきたとき、彼が実際に言いたいのは「なぜApolloなのか」ということだったのだと思います。その答えは簡単です。誰もREST APIを中心としたしっかりとしたエコシステムを構築しようとしなかったからです。オープンAPI仕様からReact.JSクライアントを生成してみてください。GraphQLクライアントが提供するものと比較してみると分かりますが、最悪です。誰もこの問題を解決するための良いビジネスモデルを見つけられませんでした。GraphQLに入ると、扱いたくない問題を抽象化するための大量のツールを手に入れることができます。

GraphQLが優れているからではありませんが、REST APIは、説明したユースケースとの関連性が低くなるでしょう。GraphQLが市場シェアを拡大し続けるのは、ツールとエコシステムのおかげなのです。GraphQLのエコシステムが拡大しているペースはRESTと比較しても早いです。

ハイパーメディアAPIは、サーバーでレンダリングされたWebアプリケーションで大きな役割を果たしてきましたし、今でも大きな役割を果たしています。しかし、ウェブは前進しています。ユーザーはWebサイトと同じくらいネイティブなエクスペリエンスを期待しています。フロントエンドではJamstackがそれを引き継いでいます。サーバーサイドレンダリングとダイナミックなJavascriptクライアントを備えたハイブリッドモデルが、これらのアプリケーションを実現しています。RESTful API は別の問題に特化しています。RESTful API がなくなることはありませんし、全くその逆だと思います!これは業界が現在シフトしているこのスタイルのアプリケーションには適したツールではありません。REST APIは、内部API、パートナーAPI、サーバー間のコミュニケーションに最適なツールだと思います。これはGraphQLがあまり得意としない分野です。RPCと並んで、この分野では大きな将来性があると思います。一方、GraphQLはリソースベースのAPIやRPCベースのAPIをラップしてくれます。

Apolloのツールがなければ、GraphQLは今のような形になっていたと思いますか?カンファレンスはどうでしょうか?GraphQL Summit? Hasuraのオンラインカンファレンス?DgraphによるGraphQL in Spaceは?The Guildによる大規模なオープンソース?GraphQL Galaxyは?

なぜGraphQLを使うべきなのか、より微妙なところを説明できたのではないでしょうか。これで、上司を納得させるための準備ができましたね。

appstore
googleplay
会員登録

会員登録して、もっと便利に利用しよう

  • 1.

    記事をストックできる
    気になる記事をピックして、いつでも読み返すことができます。
  • 2.

    新着ニュースをカスタマイズできます
    好きなニュースフィードをフォローすると、新着ニュースが受け取れます。