TL;DR
いつもの読書ログです、読了しました。
初めてのGraphQL ―Webサービスを作って学ぶ新世代API
- 作者:Eve Porcello,Alex Banks
- 発売日: 2019/11/13
- メディア: 単行本(ソフトカバー)
この本を読むことで得られる期待値
- GraphQLを採用するメリット・デメリットが理解できる
- 何故GraphQLが開発されたのか説明できる
- 自社プロダクトに組み込むと想定したときに具体的にどこで効きそうか、あるいはどの辺りは効かなさそうか判断できる
- GraphQLいくつかのAPI共通で利用される機能(ファイルアップロード/ダウンロード、ログイン/ログアウト、アクセス権限制御など)をどう実装するか
- N+1問題などのフロントエンドがクエリを組み立てることで発生する問題にどう対処するのか?
以下、感想
GraphQLはデータのあり方をクライアントサイドから再定義することで従来のサーバーサイドが行っていた不可侵領域を崩した。 これは今までサーバーサイド側の責務だったデータ設計がクライアント主体になることでクライアント側の問題を図る意図があった。
これは画面設計を行うときにデータ設計の不備が見つかることがあったので割と納得感がある。
書籍で明言されてなかったと思うがGraphQLはクライアント・サーバーサイドのどちらかに責務を寄せるのではなく、柔軟なレイヤーを一枚挟みたかったのではないかと思う。 その柔軟なレイヤーを共有することで責務がレイヤーという抽象化された概念に集約され、結果としてインターフェイスとしての責務が明確になる、みたいな。 …というようなことを読みながら考えていた。
反面、クライアントからみたデータ設計になるため、いままでとは異なる問題がサーバーサイドで起こるのではないか?という懸念をこの時点で抱いている。
GraphQLは「クライアントとサーバーサイドのパフォーマンス向上」と「データ構造の要件を満たす解決策」を目指して開発された。 つまりこれによって副次的にクライアントの開発速度が向上することが見込まれたり、エンドポイント管理の複雑さから開放される、というようなことが期待できる。
ただ結局のところ、最も目指したかったところはパフォーマンスの向上、そのためのデータ構造のユビキタス化なのだろうと思う。
GraphQLによって以下のような状態が解消されると書籍では紹介されている。
- 過剰な取得、
user.name
がほしいだけなのにuser.*
が取れてしまうみたいなやつのこと。 - 過小な取得、今度は逆に
user
が持つアイテムが知りたいだけなのにinventry
→favorite
→item
みたいに複数回リクエストしてしまうみたいなやつのこと、らしい。 - エンドポイント管理の煩雑さ、RESTなサービスの難点の1つにRoutingがあると思う。GraphQLではエンドポイントは単一なので管理そのものが実質的になくなるためこのURLは実態を表すのに相応しくない、というような喧々諤々な言い合いがなくなる。
書籍内では「GraphQLはただの仕様(Specific)だ」と紹介されている。 これはクライアントとサーバーの仕様を定義するものでしかなく、アーキテクチャのようなものではないと理解している。 その分根本的な解決には難しいケースがありそうだと思う反面、取り入れやすさ(変更しやすさ)があるのだろうと理解した。
GraphQLのクライアントはサーバーサイドとはまた別に目的があるらしい。
- 開発チームの作業効率の向上
- アプリケーションのパフォーマンス向上
この2つが主となる目的だと書かれていた。 それを実現するために「ネットワークリクエストデータのキャッシュ」、「ユーザインターフェイスへのデータ注入の肩代わり」が行えるようになっているそうな。 (そういえばユーザインターフェイスのデータ注入の肩代わりって具体的にどういうことだったんだろう?)
GraphQLには大きく分けて2派閥あってRelayとApolloというフレームワークがあるが書籍ではApolloを使われている。
そもそもGraphQLの特性とはなにか? 元々グラフ理論というものがあり、その文脈として「グラフは多くのデータを必要とするアプリケーションと相性がいい」と書かれている。 有向グラフ、無向グラフあたりで調べると良いかもしれない。 書籍にも簡易だがイメージがつくようにわかりやすい紹介があったのでグラフ理論の名前を聞いたことがない、あるいは名前しか知らないという人でも安心して読めると思う。 少なくともぼくはそうだった。
GraphQLには大きく3つの機能がある
- Query(取得)
- Mutation(更新)
- Subscription(購読)
わかりやすくいってしまえば
QueryはSQLにおけるSELECT
文のことだし、
MutationはINSERT/UPDATE/DELETE
文のこと。
SubScriptionはデザパタにおけるPubSubのこと。
リゾルバはDBだけを参照するものではない。内部APIや外部APIから受け取ったレスポンスなどを返すことができる。
だとするとクライアントがクエリを組み立てるのに懸念があるケースはDBモデルではなく専用に用意したリゾルバを参照されることで実装的には内部APIを叩いて負荷の少ない実装結果を参照させることができるのではないか?
→後述される複雑さ(complexity)で解決しようぜ!って言われてたのでこの対応は筋が悪いのかもしれない。でも以前みた GraphQLでWebAPIを作っているでは graphql-batch
使え!みたいな話があった。これはそもそもの比較がおかしいとは思うんだけどリクエストさせない対応が複雑さで、とはいえN+1発生するようなリクエストって起こり得るよね!ってときに使う解決策が graphql-batch
なのかなーって思いましたまる
mutation時にバリデーションエラーが発生したらどうするのだろう?
→明確な答えは書かれていなかったがGraphQLにおけるエラーハンドリングの仕方を読んだ感じ 200 OK
を返しつつmessage
にエラーメッセージを詰める……みたいな対応するしかなさそう。
GraphQLにおけるテストは通常(既存)のテストと同じでよいのか?それとも気をつけることがあるのか? →特にテストへの言及はなし。pockeさんが書いてくれているGraphQLのクエリを自動的にテストするというのが参考になりそう。エンドポイントが1つしかないので今までと同じ……というわけにはいかなさそう。
トリビアルリゾルバを最上位に追加するという説明で最上位にズラズラ書いてしまうのは嫌だなーという気持ちになり、なんかでまとめられないかなーとか思ってた。 →これに関しても言及がなかったけど多分探せば何かしらあるはず。
GraphQLでDatetimeはどう扱われるのか?タイムスタンプ?それとも文字列? →カスタムスカラー型を定義しましょう、と書籍で紹介されていたのでそれ読めばよい。
GraphQLでファイルのアップロード/ダウンロードが行えるのか? →結論からいうと行える、どうやるかについては書籍内の7章で言及されているのでそこ読めばよい。
apollo clientのキャッシュはデフォルトローカルに保存されると書いてあったが保存可能な最大サイズはどれくらいか?またそれはどこに保存されるのか?キャッシュ先をRedisなどに指定することは可能なのか? →明言されてないがどうにもInMemoryっぽい雰囲気。ただlocalStorageなどを指定もできる?という感じではっきりしていない。Apolloの公式ドキュメントをあたるのが良さそう。
apollo clientのキャッシュはページ遷移しても有効なのか?失効するタイミングやそのコントロールはどうするか? →InMemoryだと仮定して話すとページ遷移してもなくならない(はず)。失効するタイミングやコントロールに関しては書籍の7章に記載があるのでそちらを参照すればよい。
fetch policy: cache-ony
ってどういうときに使うもの?キャッシュがなければ例外が投げられるということだけどキャッシュがなかったら普通はリクエストしてデータをみにいくものじゃない?どういうケースのときにこれを使うことがあるの?
→Queryコンポーネントなどをキャッシュする(サーバへの問い合わせがいらないもの)のときに使われる、ということだった。これも書籍に記載されている。
まとめ
基礎の基礎は理解できたと思う。 いくつか細かい疑問などはあるのだけどそれは実際に触ったり公式ドキュメントを読んだりしていけば解消しそうだなという気がしています。
この書籍ですが非常に読みやすい日本語だったことと、イメージの説明の仕方がうまいなと感心しながら読んでいました。
この書籍を読んで期待することのうち4/5は達成されたと思います。
N+1問題などのフロントエンドがクエリを組み立てることで発生する問題にどう対処するのか?
これに関してはもう一度↓のエントリなどを読み直して情報を整理しようと思います。 恐らくそれでほぼほぼ疑問に思っていることは解消するはず。