ENECHANGEの Yuto Ono です。エンジニアリングマネージャーをしながら、フロントエンドの開発もしています。
最近、新たなプロジェクトが立ち上がり、フロントエンド、バックエンドともにTypeScriptで開発しています。TypeScript好きの僕にはたまらない環境で、楽しく開発しています。
フロントエンドは React + React Router、バックエンドは NestJS + Prisma を採用しており、フロントエンド・バックエンド間の通信には REST API を使用しています。NestJSの機能で OpenAPI 形式のファイルを出力し、ライブラリでフロントエンド側から呼び出せるAPIクライアントを作成しています。
今日はそのAPIクライアントを作成するライブラリを、 OpenAPI Generator から Orval に乗り換えた話をしようと思います。
OpenAPI Generator の使いにくいところ
最初は、定番で、社内でも使用例のある、 OpenAPI Generator を使ってAPIクライアントを出力していました。しかし、それだと使いづらい点がいくつか出てきました。
※ あくまで僕が取り組んだプロジェクトで使いづらかったという話です。OpenAPI Generator を批判する意図はありません。
JRE (Java Runtime Engine) が必要
OpenAPI Generator はJavaで作られているため、動かすにはJavaのランタイムが必要になります。せっかくフルTypeScript環境で開発しているのに、 OpenAPI Generator を動かすためだけに、Javaのランタイムをインストールする必要が出てきて、環境構築の手間が増えてしまいます。
データフェッチの際に useEffect と組み合わせる必要がある
OpenAPI Generator はあくまでもAPIクライアントの生成に特化しているため、実際にReactのコンポーネントでデータフェッチを実装しようとすると、下記のように、useEffect と組み合わせる必要があります。必要以上に複雑で、コードが読みにくいです。
const [userProfile, setUserProfile] = useState(); useEffect(() => { const loadUserProfile = async () => { const userProfile = await apiInstance.getUserProfile(); setUserProfile(userProfile); }; loadUserProfile(); }, []);
ちなみに筆者は 最近遭遇した useEffect のアンチパターンを紹介 【React】 でも書いたように、不要な useEffect は避けたいと思う派です。データフェッチの useEffect はコードが読みづらくなるため、できれば避けたいです。
なお、React19で利用可能になったサーバコンポーネントを使用すると、普通に await が使えるため、かなりシンプルな記述にできます。とはいえ今回はSSRしない構成なので、残念ながらサーバコンポーネントは使えません。
SWR等のライブラリと組み合わせて使うと、キーの管理が必要になる
SWR や TanStack Query 等のデータフェッチ系のライブラリを使用すれば、 useEffect の使用は避けられます。
const { data, error, isLoading } = useSWR("GET /user-profile", () => apiInstance.getUserProfile());
しかし別の問題が発生します。キャッシュキー(上記の "GET /user-profile"
に相当)を、自分で書く必要があります。ということは、ヒューマンエラーが発生する可能性があります。コピペしてキーを変え忘れて重複してしまったなんてことはあるあるです。
なぜか Type Error が出る
OpenAPI Generator が出力したファイルがなぜか型エラーを出していました。時間の関係で詳しい原因や解決方法は判明していませんが、調べたら同じ症状の方がいそうだったので、 TypeScript 向けジェネレータの不具合なのかもしれません。
実行時は問題なく動くので、ひとまず、自動生成されたファイルの先頭に // @ts-nocheck
コメントを挿入するスクリプトを書いて乗り切りました。とはいえ余計なスクリプトを書いている感があって気持ち悪かったです。
ここに挙げた以外にも不満・不安な点はあるのですが、キリがないのでこの辺にしておきます。憶測ですが、Java製のライブラリのため、TypeScriptと相性が良くないのかもしれません。Javaのプロジェクトだと使いやすい可能性もあることは補足しておきます。
Orval に移行したら幸せになった
そこで、上記の問題を解決すべく調査したら、 Orval というライブラリを見つけました。良さそうだったので、さっそく導入したところ、上記の問題がすべて解決されて幸せになりました。
JRE が不要に
Orval は TypeScript で作られているので、Node.js の環境があれば動かすことができます。これでJavaのランタイムをインストールする必要がなくなりました。
SWRのフックを自動生成してくれる。キーの手動管理も不要に
Orval ではSWRのフックを出力するよう設定することができます。これにより、キーの手動管理から解放され、ヒューマンエラーも防ぐことができます。
const { data, error, isLoading } = useGetUserProfile();
上記の設定をした場合も、下記の通り通常の APIクライアントも生成されるため、 OpenAPI Generator の上位互換という印象を受けました。
const userProfile = await getUserProfile();
他にも TanStack Query などにも対応しており、非常に便利です。
Type Error が出ない
Orval に乗り換えたら Type Error が出なくなったので、余計なスクリプトを削除できました!
活発に開発されている
いくら便利なライブラリでも更新が途絶えていると安心して使うことができません。Orvalは、直近(2024/12時点)、月1ぐらいのペースで新しいバージョンがリリースされているので、今のところ安心して使えます。
その他のメリット
他にも、Orval を調査する中で下記のメリットがありそうでした。しかし、まだ今はそれらのメリットを活かせていないので、今後それらのメリットが活きてくるようにしていきたいです。
- OpenAPI Generator と比べて実行速度が速いらしいという話を見かける
- 今はまだ実行速度の差を体感できるほどの規模ではない
- Mockの自動生成機能があるのでテストしやすい
- 今の段階では実装を優先してフロントエンドのテストを後回しにしているので、まだ活用できていない
さいごに
Orvalに乗り換えてから、あらゆる面で便利になり、開発体験が爆上がりしました。あきらめずに、より良いライブラリを探したかいがありました。
弊社ではOrvalの導入事例は、おそらく初めてです。年末に社内のLT大会があるので、そこでOrvalの良さを布教していこうと思います!