GraphQLは90%のウェブサービス開発者にはまだ時期尚早ではないか

PySpa統合思念体です。チャットで話をしたことのまとめです。何人かで雑に話をしたことのまとめで、特定の誰かの発言というわけではなく、一種の怪文書です。

さて、GraphQLが世間を賑わせ始めています。Facebookが開発し、GitHubも機能提供をし始めました。GraphQLはRESTの未来か？みたいな論調もありますが、新しいものが出てくると既存のものをサンドバックにして「まだそんな古いの使ってやがるのかよwwwww」みたいな煽りをするのはもはやウェブ界隈の風物詩になっていますが、そういう信者発言をして「ああ、あいつまた踊らされてるな」「あいつ技術を見る目がないな」みたいに思われないように、少し冷静にGraphQLの立ち位置や、今後予想される流れについて考えてみます。

LSUDsとSSKDs
===================

WebAPI The Good Partsでも紹介されていた概念として、Netflix社のAPI担当ディレクターが[記事で発表したもの](https://thenextweb.com/dd/2013/12/17/future-api-design-orchestration-layer/#.tnw_2HgcnaPz)があります。WebAPIは使うユーザーによって大きく2つに分類できます。

* LSUDs(large set of unknown developers): 大多数の未知の開発者が利用する
* SSKDs(small set of known developers): 小数の知っている開発者が利用する

はい。ここまで読んで先の流れが読めた察しの良い方はここでページを閉じていただいて大丈夫です。

FacebookもGitHubも、前者を対象にしています。APIを公開することにより、自社の基盤サービスを中心としたエコシステムを作り上げ、さらに魅力のあるサービスにする、ための武器です。未知の外部の開発者が利用するため、どのデータがどのように利用されるのかわかりません。また、ユーザーはロードバランサーの外の遅いネットワークにいます。そのため、サービス側としてはたくさんの種類のデータを必要に応じて返せるようにして、ユーザーが必要なデータだけを取り出すのは利益にかないます。

一方、社内マイクロサービスにアクセスする時にはまずデータ量を減らすことに対するインセンティブが減ります。つまりGraphQLのメリットは弱まります。また、使う対象もある程度は把握できるので、最初からそこまで大量の使うかどうかも分からないデータをレスポンスに乗せない、ということもできます。

なお、RESTに対して「サイズが小さくできる」というのが圧倒的な強みみたいに言われますが、RESTだって同じことをやっちゃいけない、ということはありません。GoogleのAPIはfieldsというパラメータを付けることで結果に含まれるレスポンスのカラムを絞る機能を提供しています。これなら既存のREST APIに追加するのは簡単にできますよね。

https://developers.google.com/drive/v3/web/performance#partial

**追記(7/1)**

他の某所のチャットで話題になったところによると、バッチでまとめてもってくるのもメリットとしてあげられるけど、SSKDsならBFF: Backend for Frontend使えばいいよね、という話もありました。また、ウェブブラウザからリクエストを送信するときにも、multipart formを使うバッチリクエストをGoogleは提供していたりします。Microsoftのガイドラインでも見かけた気がしたけどソースがみあたりませんでした。

https://developers.google.com/drive/v2/web/batch#format-of-a-batch-request

クラサバ時代に戻るデメリットとメリット
===================================

GraphQLはQuery Language。つまり、SQLみたいなものを直接発行するようなものです。実際、シンプルで分かりやすいSQLのような雰囲気があります。これはクラサバ時代とやっていることは同じよね。XMLDBみたいなのもありましたよね。歴史は繰り返します。ですが、ある意味少し現時点では後退しています。

クライアント側のDeveloper Experienceは確かにいいのかもしれません。パーサーが用意されており、文法チェックをしてくれるエディタのライブラリもあるんでしたっけ。ではサーバー側は、というとSQLのエンジン相当を各サーバーの実装者が書かないといけない、ということです。パーサーはあるでしょう（少なくともNode.jsには）。クエリーを見てストレージエンジンにアクセスして結果を整形して返すところは実装が必要です。

ID相当の部分だけを見てSQL（or ORマッパー）で取ってきて、サーバー側でフィルタリングというのはまあ悪手ですよね。無駄に大量の負荷をデータベースエンジンにかけるし、CPUブロッキングを起こしがちなNode.jsでCPU負荷のかかる処理をするのはソフトウェアエンジニアリングの敗北です。うまくやるには、GraphQLのクエリーから、無駄なアクセスをしないSQLを組み立ててサーバーに投げる、ということが必要ですが、まあ手間はかかりますよね。フィールドのフィルタリング程度ならまだしも、階層構造をきちんと扱う中間層をつくるのは大変そうです（作ったことがないので想像ですが）。

GraphQLが圧倒的な勝者になる条件が１つあります。それはGraphQLをネイティブで実装したミドルウェアやデータベースエンジンが登場し、サーバーコードを実装しなくてもGraphQLが簡単に扱えるようになることです。SQLを使うRDBのGraphQL版です。

ただ、これはまったく新しい発想ではなく、CouchDBもこのような発想でスタートしていたはずです。独自のJSONベースのクエリーというとMongoDBもそうですね。薄いウェブサービスレイヤーが内蔵されててJSでストアドプロシージャが書けるようなノリのArangoDBもあります。PostgreSQLのJSON対応も進んでいますし、C#でストアドプロシージャが書けるSQLServerもあります。こういうのが進化して、GraphQLをネイティブサポートするなどすれば、サーバー実装負荷とかパフォーマンスとか考えずに使えるようになります。データベースをインターネットに晒す格好にはなりますが・・・既存のストレージエンジンにつなぐミドルウェアや、ORマッパーのようなGraphQL-RelationalDBマッパーとか、そういう方向性もありそうです。

と、思ったらPostgreSQL用のはありました。

https://github.com/postgraphql/postgraphql

まとめ
======

作っているサーバーのユーザーはLSUDsでしょうか？それなら今努力してGraphQLを実装する価値はあるかもしれません。少なくともGithubとFacebookがバックにいるので１−２年で陳腐化して捨てられることはないはず。

作っているサーバーのユーザーはSSKDsでしょうか？それなら今は使う必要はないと思います。利用しているウェブサービスにアクセスするためのクライアントとしての利用だけで我慢しておくほうがいいんじゃないでしょうか？あるいは簡単に使えるミドルウェアやストレージエンジンが出てくるまで待つほうがいいでしょう。


**(7/1)追記**

僕の周りではGraphQLを擁護する声は無かったけど、課題は「サーバー開発工数（複雑さがここに集中してメンテナンスとか大変かも？というのも含めて）」の一点なので、ここを論破すれば状況は変わりますので、反論エントリーお待ちしています！

**(2019/10/01)追記**

いろいろGraphQLサポートしたDBMSが出てきていますね。

* https://supergraph.dev/
* https://www.graph.cool/

**(2020/03/24)追記**

GraphQL EFF的なものが出てきたそうです。こういう世界観を待っていました。本来はクエリー変換を手実装する原始人みたいなことをやる必要はないはず。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">GraphQL Code Generatorを開発しているThe Guildから新しくGraphQL Meshがリリース<br><br>gRPC, OpenAPI, Swagger, SQLなどに対してGraphQLクエリーを実行可能にする <a href="https://t.co/2hBy8A2Jb7">https://t.co/2hBy8A2Jb7</a></p>&mdash; そな太@GraphQLはいいぞ (@sonatard) <a href="https://twitter.com/sonatard/status/1242228969652412416?ref_src=twsrc%5Etfw">March 23, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>