GraphQLの理想と現実

うち(obniz)でも採用してるGraphQLの人気がなくなってきてるらしい。

本当かどうか置いといて、正直気持ちはわかる。

僕も新しい小システムでは採用せずRESTに切り替えたし。

ただ、GraphQLとしてきっとこういうこと目指したいんだろうなっていう理想とそれに対する差が大きいからその難しさを解消しないといけない。

自由と堅固さの両立

なんでGraphQLがいいなと思って採用したかって、もちろん新しいからという新しいもの好きなのもあるけど、REST APIが持っていたフォルダ構造の限界を打ち破れそうな気がしたから。

まず、REST APIだとあるユーザー`yukisato`が投稿してしかもお気に入りにした記事を取り出そうと思ったら、例えば

GET /users/yukisato/articles/liked

だったり

GET /articles/liked?by=yukisato

なんてのだったり、書くわけだけど実際は、その記事に対して書かれたコメントも欲しいなんてことになると、もう1段階深いところまで返すことがあって

{
  articles: [
  {
    id: 101,
    text: "焼きそばの青のりは必須?",
    comments: {
      user: whiterabbit,
      text: "いらない"
    }
  }
  ]
}

なんて多段になることもある。

そこで思うのは

「あれ、userを取りに行ったのにuserのしたに記事があるの?」

「articleの下にコメントがあるの?」

のような妙な感覚がでてくる。この原因は URLのパスに上下関係があるからで

/users/yukisato/articles

を見たときに、「usersフォルダがあって、その下にyukisatoがあって、その中にarticlesがある。」用に見える。

これはwww(ワールドワイドウェブ)の名残で、本当はこんな書き方じゃなくていい。だってフォルダでもファイルでもないんだから。だけど踏襲してる。ブラウザが過去を切り捨ててなくて、むしろそれに合わせようぜとなっているのが REST APIだから。

だから、フォルダ構成っぽくないものとか(/articles/today)、2段以上( articleの中にcommentsがある )になると「ん?」となる。いくら規格化して方針を定めてもURLに構造があって1つである限り表現に限界がある。

GraphQLはそれを打開しようとしているのをすごく感じる。

まず階層構造はない。すべて平おき。まず記事を取ってくるのはこのようになる。

query getArticles(id: "yukisato") {
    id
    text
  }
}

まず、前提としてAPIサーバー側に`getAticles`が必要になる。関数を用意するのと同じ。そして要求に対して返す。今回はidとtextを返せば良く、他に情報があったとしてもいらないと言われている。

GraphQLという名前の通り、SQLがすごく意識されていて

select id, text from articles where id = 'yukisato';

と同じことになっている。

SQLもTable同士に階層構造がないように、GraphQLにもない。

これがものすごい自由度と堅固性を担保している。

更にjoinやサブクエリを目指しているような仕組みがGraphQLにあって、同時にクエリを叩くことができる。例えばREST APIで記事とそのコメントを取り出したかったら

  1. コメントも帰ってくる記事APIを叩く
  2. 記事APIを叩いた後に関連するコメントAPIを叩く

といった動きになるが、GraphQLでは

  1. 記事APIと関連コメントAPIを同時に叩く

ことができる。しかし、ここに問題がある。「それを実装するのが自分」ということ。

SQLのような自由度をWebAPIに持ってきたのはいいけど、結局それを全部自分で実装しないといけない。記事APIと関連コメントAPIを同時に多々たたけるようにするのは自分ということになる。

いくらGraphQLで「構文として」それを許可したとしても実装されていないと意味がない。

もちろんライブラリのサポートはある程度は存在しているが結局認識しているクエリじゃないと対応が難しく、場合によっては無限に掘り下げる実行をしてしまう可能性がある(これは昔から指摘されていて、限界回数を指定できるなどの仕組みがライブラリにある)

そうなると結局REST APIと同じような作りになる。誰も同時にクエリを叩いたりせず1つずつ叩くし、REST APIのendpointを作るようにqueryやmutationを作ることになる。

そんなことするぐらいならドキュメント生成ツールがあったり、エラーが読み取りやすいREST APIの方がいいってことになってしまう。

今GraphQLが下火になってるのなら、REST APIを置き換えて使ってみたけど「これREST APIでよくね」となっているんじゃないかという気がしてしまう(僕がそうなってる)。

GraphQLを提供するDBができればいい

解決策として思うのは、そもそもデータベースがSQLではなくGraphQLでtable操作ができ、それを公開すればアプリを書くこともなく使えるというのが理想だと思う。

そもそもデータを操作するのにAPIである必要はなくフロントエンドからSQL叩いたっていいはず。firebaseとかsupabaseがそれに近いことをやっている(Application less database accessともいうべき)。

問題になるのは認証やマイグレーションや負荷対策などだろうけど、もう少しロジックをデータベース側に持たせてGraphQLが生えて操作ができるのであればきっと理想に近づけるんだろうなと思う。