ベルリンのITスタートアップで働くジャバ・ザ・ハットリの日記

日本→シンガポール→ベルリンへと流れ着いたソフトウェアエンジニアのブログ

GraphQLを最速でマスターするための意識改革3ヶ条

移転しました。

GraphQLをサーバー側とクライアント側とで実装してみて得た意識すべきポイント3つについて。

  • ひとつのエンドポイント
  • バージョン無し
  • できるだけ薄く

この3つを意識して実装するのとそれが無いのとでは実装スピードが何倍か違うと思う。特にREST脳な人の場合。

GraphQLは使い所さえあえばめちゃくちゃに有用で面白い。しかもGraphQLの公式サイトはとてもわかりやすく説明されている。その辺のブログ記事よりもよほど洗練されていて、技術に関してはここ以外に読む必要はほぼ無い。

しかしGraphQLを使いはじめた当初はなんか妙にコーディングが進まなかった所があった。その原因はずっとRESTfulでやってきたREST脳の意識からGraphQLへと変換できていなかったことだった。GraphQLとRESTは設計思想が異なるので、意識を変える必要がある。その意識さえ変えればGraphQLに難しいところなんて無く、とてもカンタンに実装できてしまう。公式サイトに技術内容がどんなに分かりやすく書かれていても、このポイントを絞って意識を変えることに注力していなかったら「あれ??今まではこうだったのに、なぜだ?」となってしまうのだ。

そもそもGraphQLってなに?となっている人はこことか公式サイトを読んでください。

GraphQLを書いてみた話 - emahiro/b.log
GraphQLに関するメモ - ushumpei’s blog
GraphQLを使ってみた - kakts-log

以下はそのGraphQLの概念は分かっているという前提で書く。

【意識改革1】 ひとつのエンドポイント

GraphQLにエンドポイントはひとつしかない。RESTのように `GET api/v1/users` `POST api/v1/users/:id` とか一切無い。どんなリクエストも全て`/graphql` に向けて飛んでくる。GraphQLのクエリ言語がそういった処理を全て吸収してくれるからだ。クライアント側を実装していて「えーっとこのリクエストはどこに投げればいいんだっけ?」と思う度に、いやいや「どこ?」とか考えてる時点でそれREST脳だ、と自分で訂正していた。

個人開発したウェブアプリをGraphQLで実装していた時にある箇所のキャッシュとデータベース側の処理を2種類に分ける必要があった。そこで通常の処理を`/graphql` で受けて、その他の処理を別のエンドポイント(例えば`/graphql2` とか)で受ける設計を考えた。今から考えてもバカなデザインだったと思う。こんなことをするとGraphQLのメリットをちっとも活かせないし、メンテナンス性が著しく下がる。後からqueryやTypeが変更になった際にいちいち2つのエンドポイントとその挙動について考えなければならない。個人でやってたのでクライアント開発者とサーバー開発者の間でやりとりする必要は無く、自分の脳内で2役やって終わりだったが、あれをチームでやってたら、エンジニア同士の間でムダな話し合いが増えていたことが分かる。

GraphQLのメリットはクライアントーサーバー間で「この型でいきましょう」と決めただけで実装にかかれるところにある。後は欲しい情報を欲しい時にリクエストするだけで完了する。
公式サイトにはサラっとしか書いていないが「ひとつのエンドポイント」はとても重要なことなのだ。

【意識改革2】 バージョン無し

GraphQLは基本的にバージョンレス。つまりRESTのようなバージョンという概念は無い。RESTでやってるプロジェクトは`api/v1/...` からバージョンがあがるとクライアント側の実装と呼吸を合わせて、せ~ので`api/v2/...` とかを作ってリリースしていた。
理論上はうまくいくはずなんだけど常にこれが頭痛の種になる。だがGraphQLにバージョンは無い。`/graphql` というエンドポイントでv1もv2も無くずっとそのままだ。

これがREST脳だった私には不安だった。「え?バージョン無し???一応は念の為に付けとこうかな?」みたいに考えた。しかしこの質問と答えで気が付いた。「RESTのAPIになぜv1やらv2とバージョンがあるのか?」答えは「破壊的な変更があって、その度にバージョンを上げる必要があるから」と。
つまりは「GraphQLにすれば破壊的な変更は無いのでバージョニングする必要がない」となる。
クライアントが欲しい情報をリクエストして、そのままレスポンスを返すGraphQLでは基本的に変更をかけても破壊的ではない。今まであったfieldを削除するとかは破壊的かもしれないがGraphQLではadd-only approachを推奨している。つまり加えるのみ。

なので今回作ったrailsのルートはBest Practice にのっとり
`graphql POST /api/v1/graphql`
とかではなくて
`graphql POST /graphql`
としている。清々しい。対外的にも「バージョンは無いからな」と宣言することにもなる。
今までRESTfulでAPIのバージョンを上げることの痛みをずっとガマンしてきた人なら、このversionless APIという発想は気に入っていただけるだろう。

【意識改革3】 できるだけ薄く

GraphQLの作者Lee Byron氏も言うように「なんでもできるGraphQLだが、GraphQLはできるだけ薄く保つのがいい」と。Lee Byron氏の公演ビデオはとても示唆に富んだビデオで参考になる。そんな中で一番はこの「できるだけ薄く保て」という箇所だった。
実装していけば認証やらセッション管理やらありとあらゆる処理がGraphQLを通して行われる訳だが、それらをGraphQL内でやってしまわないこと。それらの処理をするのに適したクラスが必ずあってそっちでやるべき。
GraphQLの責務と役割はquery(RESTfulのGET)とmutation(POSTもしくはPATCH)だけであって、それ以外は無い。したがってresolve ->(obj, args, ctx)の中にロジックがあったら、その責務はどこが持つべきか考えてそこに移すべき、ということだ。
これはアプリのソースコードが大きくなればなるほど効いてきた。「薄くしといてよかったなー」と。

以上がGraphQLを最速でマスターするための意識改革3ヶ条でした。

動くGraphQLの実例

こちらがGraphQLとRails、React+Reduxを使って個人で開発したウェブアプリです。
エンジニアの集合知を図解するSNS「node-node-node」デスクトップ版
PCでしか見れませんが、ぜひ感想をお聞かせください。ぶっちゃけ、どう改良したらいいのか分からなくなってきたので「ここがダメなんだよ」とかなんでも言っていただけたらありがたいです。

情報ノードはパラっと開いて、グリグリとドラッグできる訳でして、こうして階層構造の技術リンクをマッピングしていけば理解しやすいかな、と。
f:id:tango_ruby:20180205204551g:plain

node-node-nodeについてなんでも感想や批評を書き込めるフォーム

少し前はいろいろとダメな点とかご指摘をたくさん受けて修正した。でもその修正が終わった途端、あんまりコメントをいただけなくなって辛いので、ちょっとでもコメントいただけたら嬉しい。

tango-ruby.hatenablog.com

www.youtube.com

tango-ruby.hatenablog.com

tango-ruby.hatenablog.com