apollo-client 周りをいい感じにする
Carely のバックエンドは基本 GraphQL で実装されていて、フロントエンドでは apollo-client を使ってアクセスしています。これらの仕組みはぼくの前の技術顧問でもある 翁さん が構成をしてくれていたもので現在のメンバーは詳しく仕組みを理解せずに使い方を覚えて実装している状態でした
GraphQL の導入から時間もたち、利用されている箇所が増えてきたことでいくつか課題が見つかってきたので対応を行いました
useQuery, useMutate の型を通す
あるプルリクエストをレビューした時に graphql-codegen された useQuery などの返り値の型が any になっている事に気づいてしまいました。よくよくライブラリを辿ってみると vue-apollo の package.json で指定されている apollo-client が peerDependencies になっており、フロントエンド側では違うバージョンのものがインストールされており型情報が途中で抜け落ちてしまっていました
適切なバージョンに置き換える事で解決はしたのですが、型情報が通った事でこれまで問題なかった型チェックが一気に落ちるようになってそれの修正がきつかったです
バージョンをあげる
リクエスト系のライブラリのバージョンアップは怖いですよね。 apollo に関しては雑に考えるだけでも以下のような依存するライブラリがあるため、厳しさが増します
vue-apollo
apollo-uploader
graphql-codegen with typescript-vue-apollo
apollo-client
etc.
とはいえセキュリティ的な問題もあるため、えいやっとバージョンをあげました
apollo-client のフェッチポリシーを設定する
Carely では歴史的な経緯から useQuery の onResult を利用したデータの取得ロジックが多く実装されています。現在はエンドポイントごとに別のアプリケーションとなる MPA 構成なので大きな問題がでていませんが、たまにキャッシュが効いてしまって想定外の挙動になってしまう事が発生していました
toB 向けでデータの更新が多いアプリケーションという事もあり、 fetchPolicy を network-only にすることで、 axios などのような通常の api クライアントと同じように取り扱えるようにしました
また、これらに関するドキュメントも書いています
GraphQL 用の型を書く
codegen された GraphQL の型を運用するにあたって厳しいと感じたのは、すべてのフィールド型が T | undefined になってしまう事です。 nullable なフィールドだとさらに Maybe<T> | undefined になってしまい、コード中にオプショナルチェイニングが溢れてしまうという悲しい状況になっていました
また、スキーマから生成した型に対してクエリから生成した型は部分型になっているため、クエリから生成した型を使いたいのですが、階層が深く型の抽出が大変(というか慣れていないと実質不可能)でした
これらへの対応として、クエリから生成した型のフィールドから再起的に required にしたジェネリクス型を作りそれで型変換するようにしました(詳しくは iCARE Dev Meetup #23 で発表した Carely フロントエンドのコード品質を支える技術 をご覧ください)。現時点では null も undefined も同等に required してしまうので危険は残るのですが、それでもずいぶん取り扱いやすくなりました
※他の組織でどうやってこれらを解決しているのか気になります・・・
より型安全な世界に
TypeScript の世界を安全にするには型をしっかり通す事が大事です。また、型を自作せずライブラリから取得していかないといけません。特に API の返り値は一番危険が発生する箇所なので apollo-client 周りをきちんとする事で一歩安全なアプリケーションへの近づけたと思います
この記事は iCARE の技術顧問がどんな事をやっているか - 2021アドベントカレンダー の17日目の記事です