こちらは バイセルテクノロジーズ Advent Calendar 2022 の 1日目の記事です。
こんにちは。開発2部の早瀬です。
自分のチームではReact(Next.js)+GraphQLという構成でフロントエンドの開発をしていて、状態管理にはApolloClientを採用しています。その中で最近フロントエンドの構成を大幅に見直す機会があり、以前から気になっていたRelayを導入しようとしたのですが、色々調査した結果、最終的には断念することになりました。
そこで今回はRelayに乗り換えようとした経緯や、なぜ断念するに至ったかを紹介しようと思います!
Relayに乗り換えようとした動機
RelayはMetaが開発しているGraphQLクライアントです。一連のGraphQL操作が行えることに加えApolloClientと同じようにキャッシュを利用して状態管理も行うことができます。実際にFacebookもRelayを使用して開発されています。 Githubのスターの数は2022/11/21時点で17.4kで、apolloClientの18.3kとほぼ同じくらいです。直近でも積極的にメンテナンスされています。
一連のGraphQL操作を行えるという点ではApolloClientと大きな違いはないのですが、それでも自分がRelayに乗り換えたいと思った理由がいくつかあります。
Fragment Colocationを強制できる
自分がRelayを導入したい理由の中で一番ウェイトを占めているのがFragment Colocationを強制できるという点です。
Fragment Colocationとはコンポーネントで使用するデータをFragmentとして定義して、コンポーネントとセットで管理するという設計思想です。こうすることでデータ要件も含めてコンポーネントをカプセル化できるので、取得データや表示要件の変更があった際に親コンポーネントに修正の影響が及ばずコンポーネント同士が疎結合になるというメリットがあります。
Fragment Colocationに関しては勉強会でも発表しているので、詳しくはそちらの資料をご覧ください。
自分はこの考え方がとても気に入っているのと、Relayを使って実装することで自然にFragment Colocationに準拠するようになっている点がかなり魅力的に感じました。ApolloClientでも別途graphql-code-generatorのプラグインを活用することで似たようなことができます。ですが、RelayではRelayを構成する重要な要素としてFragment Colocationが位置付けられています。そのためuseFragment
やusePaginationFragment
といったhooksが提供されていてApolloClientよりも圧倒的にFragment Colocationの思想に沿った実装を行うことができます。
コンパイラを内包している
ApolloClientではgraphql-code-generatorなどを利用してTypeScriptの型情報やhooksを生成することが多いと思いますが、Relayではコンパイラが内包されています。そのため外部のライブラリなどを使用しなくてもQueryの定義などから型情報を生成してれたり、コンパイル時にランタイムで使用するQueryの最適化などを行ってくれます。
またRelayではQueryやFragmentの命名規則が決まっており、コンパイルの段階でその命名規則に沿っていない場合はエラーを吐くようになっています。これによりチーム内で命名規則に関するルールを作らなくても統一された命名になるので、チーム開発においてコードの統一性の維持も楽になります。
Reactと相性がいい
ReactもRelayどちらもMetaが開発しているということもあり、Reactとの相性がかなりいいです。具体的にはReact18で正式リリースされたSuspenseなども、Relayではそれ以前から対応していたりとReactの新機能への追従が他のクライアントライブラリと比べても早いです。
これによりReactの良さを最大限発揮することができるのもRelayの良さだと思います。
乗り換えられなかった理由
上記の理由から実際にRelayに乗り換えようとしたのですが、実際に導入するにはいくつか問題があり最終的に断念することになりました。
HasuraがRelayの要求するスキーマを満たせない
導入を断念するに至った主な理由としてはGraphQLサーバーとして採用しているHasuraにあります。HasuraとはDBのスキーマからリゾルバを自動生成してくれて、簡単にGraphQLサーバーを立てることのできるOSSです。
詳しくはこちらの記事をご覧ください。
Hasuraを使うとリゾルバやスキーマを自動生成でき、サーバーサイドの実装工数をかなり削減できるため採用していたのですが、Relayが要求するスキーマの仕様をHasuraが自動生成するスキーマでは満たせてない部分がありました。
GraphQLの仕様ではidフィールドはグローバルに一意であることが推奨されており、Relayもスキーマがその仕様に準拠している前提になっているのですが、Hasuraではその仕様が満たせていませんでした。Hasuraのスキーマではidフィールドは単純にテーブルのidカラムに相当するのですが、自分のチームではidカラムは基本的にauto-incrementなので、テーブル内ではユニークですがグローバルではユニークにはなっていません。
この仕様を満たすために全てのテーブルのidカラムをuuidに変える方法もありましたが、流石にその変更は影響範囲が大きいのと、Relayを使うためだけにDBのスキーマを変えるのもやりすぎかなと思ったのでその方法は不採用にしました。
もう一つHasuraが提供してくれているRelay用のエンドポイントを使用するという方法も候補としてありましたが残念ながらこちらも採用には至りませんでした。
このエンドポイントでは、idの値やテーブル名などの組み合わせをbase64でエンコードしてグローバルに一意な値をidフィールドとして返してくれたり、Relayで推奨されているページネーションの仕様であるCursor Connectionsに沿ったスキーマを提供してくれます。
こちらはRelay用のエンドポイントなのもあり、idの問題を解決してくれる上にページネーション用のスキーマも提供してくれるのでほぼ完璧に近いのですが、この機能自体まだbetaなのと、totalCountが取れないなど本番運用を考えると機能がちょっと足りないため断念せざるを得ませんでした。
このRelay用のエンドポイントに関しては関連するIssueがいくつか上がっていて需要自体はありそうなのと、個人的にももう少し機能が充実してきたらかなり使いたいと思っているので今後に期待といった感じです。
Relayに関するドキュメントが少ない
Relayへの乗り換えを諦めた理由としては上記がほとんどなのですが、それとは別にドキュメントが少ないのが気になりました。ApolloClientと比べると圧倒的に少なく特にNext.jsの例がほとんど検索にヒットしなかったので、後からjoinした人の学習コストなどを考えると少し採用しづらさはあるかなと思いました。
ただ個人的にRelayの思想はかなり好きなので、今後もRelayに関する記事を積極的に書いて少しでもコミュニティの形成に貢献できたらなと思います!
最終的にどうしたか
上記に記載したように残念ながらRelayの導入は断念せざるを得ませんでしたが、元を辿ればRelayの導入を考えた一番のモチベーションはFragment Colocationを強制できるという点でした。そこでRelayへの乗り換えが難しいと結論が出てからは、ApolloClientでFragment Colocationを快適に行える環境を整える方向にシフトしました。
現時点はすでに各種ライブラリの導入や開発ルールの整備などは完了していて、思ったよりも快適に開発ができているので、このあたりに関してはまた別の記事で紹介しようと思います。
まとめ
今回はすでにある程度プロダクトができている状態からRelayへの乗り換えを試みたのでいくつか問題がありましたが、最初の技術選定のタイミングであったりGraphQLサーバーを自分たちで実装している場合であればスムーズに乗り換えができたのかなと感じました。
Relayはまだドキュメントが少なかったり制約が多かったりもしますが、それ以上に魅力があるライブラリだと思うのでまた別の機会で採用できたらなと考えています。
最後にBuySell Technologiesではエンジニアを募集しています。興味がある方はぜひご応募ください!
明日のバイセルテクノロジーズ Advent Calendar 2022は藤澤さんの「スクラムを通じてチームの生産性が爆上がりした話」です、そちらもぜひ併せて読んでみてください!