APIスキーマから型を生成してフロントエンド開発を安全にする
はじめに
フロントエンドとバックエンドを分離した構成では、APIのレスポンス仕様とフロントエンド側の型定義がずれることがあります。
特に、既存システムを運用しながら新しいフロントエンドやAPIを追加していくプロジェクトでは、画面、API、バッチ、インフラ、旧実装が並行して存在します。
そのような環境では、「このAPIは何を返すのか」「このフィールドはnullableなのか」「この値は本当に存在するのか」といった確認が頻繁に発生します。
そこで有効なのが、APIスキーマからフロントエンド用の型を生成するアプローチです。
この記事では、既存プロジェクトでの調査・実装経験をもとに、APIスキーマから型を生成するメリット、導入時の注意点、生成AIとの相性について整理します。
なぜAPIスキーマから型を生成するのか
フロントエンド側でAPIレスポンスの型を手書きしていると、バックエンドの変更に追従しづらくなります。
例えば、以下のような問題が起きます。
- API側でフィールド名が変わったが、フロントエンドの型が古いまま
- nullableになった値をフロントエンドが必須として扱っている
- レスポンスに存在しないフィールドを参照している
- 同じAPIレスポンス型が複数箇所で重複定義されている
- 仕様確認のたびにAPI実装やドキュメントを読みに行く必要がある
小さなプロジェクトであれば手動管理でも回るかもしれません。
しかし、複数画面・複数API・複数サービスが絡む構成では、手書きの型定義は徐々に信用できなくなります。
APIスキーマを起点に型を生成すると、フロントエンドはバックエンドの契約に沿って実装できます。
型生成で得られるメリット
一番大きいメリットは、APIとの境界が明確になることです。
フロントエンドの実装中に、レスポンスの構造を推測で扱う必要が減ります。
生成された型を見れば、どのフィールドが存在し、どの値が必須で、どの値がnullableなのかを確認できます。
また、API仕様が変わった場合も、型生成を再実行することで差分に気づきやすくなります。
例えば、あるフィールドが削除された場合、フロントエンド側でそのフィールドを参照している箇所がTypeScriptのエラーになります。
これは実行時の不具合になる前に、ビルド時点で検知できるということです。
型生成によって得られる効果は以下です。
- APIレスポンスの扱いを型安全にできる
- 仕様変更による影響範囲を検知しやすい
- フロントエンド側の重複型定義を減らせる
- APIの利用方法をコード上で確認しやすい
- 生成AIやIDEによる補完が効きやすくなる
生成された型をそのまま使いすぎない
一方で、生成された型を画面全体に直接広げすぎると、API仕様の変更がUI実装に強く波及します。
APIレスポンスの型は、あくまで「通信境界の型」です。
画面表示に必要な形とは一致しないことがあります。
そのため、実装では次のように分けて考えるのが安全です。
// APIから返る型
type ApiArticleResponse = {
id: number;
title: string;
categoryName: string | null;
publishedAt: string | null;
};
// 画面で扱いやすい型
type ArticleViewModel = {
id: number;
title: string;
categoryLabel: string;
publishedDateLabel: string;
};
APIレスポンスを受け取ったら、画面用のViewModelに変換します。
function toArticleViewModel(article: ApiArticleResponse): ArticleViewModel {
return {
id: article.id,
title: article.title,
categoryLabel: article.categoryName ?? "未分類",
publishedDateLabel:
article.publishedAt === null
? "未公開"
: new Date(article.publishedAt).toLocaleDateString("ja-JP"),
};
}
こうしておくと、APIのnullableや命名に画面側が引きずられにくくなります。
生成型はAPI境界で使い、画面内部では用途に合わせた型へ変換する。
この分離があると、フロントエンドの見通しが良くなります。
型生成は「契約」を可視化する
APIスキーマから型を生成する目的は、単にTypeScriptの型を楽に作ることではありません。
重要なのは、バックエンドとフロントエンドの契約をコード上に可視化することです。
APIスキーマがあることで、次のような確認がしやすくなります。
- このエンドポイントはどのリクエストを受け取るのか
- レスポンスにはどのフィールドが含まれるのか
- 必須項目と任意項目はどれか
- enumとして扱うべき値は何か
- エラー時のレスポンスはどう扱うのか
既存プロジェクトでは、仕様がドキュメントだけに存在するとは限りません。
実装、設定、運用、古いコードの中に仕様が分散していることがあります。
その中で、APIスキーマはフロントエンドとバックエンドの接点を整理するための重要な材料になります。
生成AIとの相性
生成AIを使った既存コード調査では、型生成されたファイルがあると調査が進めやすくなります。
例えば、AIに次のような調査を依頼できます。
この画面で使っているAPIレスポンス型を確認し、
nullableなフィールドがUIで安全に扱われているか見てください。
このAPI型を使っているコンポーネントを洗い出し、
仕様変更時に影響しそうな箇所を整理してください。
生成されたAPI型と画面用の型が混ざっていないか確認してください。
AIはコードベース全体を広く探索するのが得意です。
一方で、AIの出力はあくまで仮説として扱う必要があります。
最終的には、生成型、APIスキーマ、実装、テスト結果を人間が確認します。
AIには調査や候補出しを任せ、人間が設計判断を行う、という分担が重要です。
導入時に気をつけること
API型生成を導入するときは、生成ファイルの扱いを決めておく必要があります。
例えば、以下のような観点です。
- 生成コマンドをどこに置くか
- 生成ファイルをGit管理するか
- CIで型生成差分を検知するか
- APIスキーマの更新タイミングをどうするか
- 生成型をどのレイヤーまで使ってよいか
- 画面用の型やViewModelに変換するルールを作るか
特に重要なのは、生成型を使う範囲です。
生成型をすべてのUIコンポーネントに直接渡すと、API仕様とUIが密結合になります。
反対に、変換層を厚くしすぎると、実装コストが上がります。
そのため、API境界では生成型を使い、画面固有の表示ロジックが増える場所ではViewModelに変換する、というバランスが扱いやすいと感じています。
まとめ
APIスキーマから型を生成すると、フロントエンドとバックエンドの境界を型として扱えるようになります。
これにより、API仕様変更の影響に気づきやすくなり、レスポンスの扱いも安全になります。
一方で、生成型は万能ではありません。
APIレスポンス型をそのまま画面全体に広げると、UIがAPI仕様に引きずられます。
実務では、以下のように分けるのが有効です。
- API境界では生成型を使う
- 画面内部では必要に応じてViewModelへ変換する
- 型生成差分で仕様変更を検知する
- 生成AIには調査を任せる
- 最終的な設計判断は人間が行う
APIスキーマから型を生成することは、単なる自動化ではなく、フロントエンドとバックエンドの契約を安全に保つための仕組みです。
既存プロジェクトで安全に開発を進めるうえで、かなり効果のあるアプローチだと感じています。