OpenAPIとTypeScriptで型安全なAPIクライアントを構築する
すべてのフロントエンド開発者が経験したことがあるでしょう。APIからデータを取得し、存在するはずのフィールドにアクセスしたら、実行時にundefinedが返ってくる。TypeScriptがこれを防ぐはずだったのに。問題はresponse.json()がanyを返すため、コンパイラがチェックする対象がないことです。このガイドでは、OpenAPI仕様から直接型を生成し、それを使用してTypeScriptで型安全なRESTクライアントを構築する、適切な解決方法を紹介します。
重要なポイント
- 手動で記述したTypeScriptインターフェースは実際のAPIから乖離し、誤った安心感を与えて実行時エラーにつながります。
- OpenAPIコード生成はAPI仕様から直接型を生成し、フロントエンドとバックエンドを同期させます。
- 軽量でfetchベースのセットアップには
openapi-typescriptとopenapi-fetchを使用し、生成されたTanStack Queryフックと完全なクライアントSDKが必要な場合はOrvalを使用します。 - 型が古くならないよう、CIまたはビルドスクリプトで型生成を自動化します。
- コンパイル時の安全性だけでは不十分な重要なエンドポイントでは、生成された型とZodのようなランタイムバリデーターを組み合わせます。
手動での型定義が破綻する理由
素朴な解決策は、インターフェースを手動で記述することです:
// ❌ コンパイルは通るが、TypeScriptはあなたを信頼しているだけ
const data = (await response.json()) as User
これにより自動補完は得られますが、バックエンドが変更された瞬間に嘘になります。型が現実から乖離し、再び実行時の予期しない動作に戻ってしまいます。
より良いアプローチ:信頼できる情報源であるOpenAPI仕様から型を生成することです。
OpenAPI TypeScriptコード生成ワークフロー
典型的なワークフローは次のようになります:
- OpenAPI仕様(YAMLまたはJSON、ホストされているものまたはローカル)から始める
- ジェネレーターを実行してTypeScriptの型または完全なクライアントを生成する
- それらの型をアプリケーションコードにインポートする
これにより、フロントエンドが自動的にバックエンドと同期します。仕様が変更されると、再生成すればTypeScriptが何が壊れたかを正確に教えてくれます。
アプローチの選択:型のみ vs 完全なクライアントSDK
OpenAPI TypeScriptコード生成には2つの主要な戦略があり、適切な選択はプロジェクトによって異なります。
| アプローチ | ツール | 最適な用途 |
|---|---|---|
| 型を生成し、fetchは自分で用意 | openapi-typescript + openapi-fetch | 軽量でfetchベースのプロジェクト |
| 完全なクライアントSDKを生成 | Orval、OpenAPI Generator | すぐに使えるフックとクライアントが必要なチーム |
型のみの生成はバンドルサイズを小さく保ち、HTTPレイヤーを制御できます。完全なSDK生成は配線時間を節約しますが、メンテナンスすべき生成コードが増えます。
OpenAPI 3.0 vs 3.1: ほとんどのツールはOpenAPI 3.0を十分にサポートしています。OpenAPI 3.1のサポートは様々です—完全な互換性を前提とする前に、ジェネレーターのドキュメントを確認してください。
Discover how at OpenReplay.com.
アプローチ1: openapi-typescriptとopenapi-fetch
これは最小限のランタイムパスです。仕様から型を生成し、次にopenapi-fetchをネイティブFetch APIの薄い完全型付きラッパーとして使用します。
npx openapi-typescript ./openapi.yaml -o ./src/api/types.ts
npm install openapi-fetch
import createClient from "openapi-fetch"
import type { paths } from "./api/types"
const client = createClient<paths>({ baseUrl: "https://api.example.com" })
// パス、パラメータ、レスポンスはすべて型チェックされる
const { data, error } = await client.GET("/users/{id}", {
params: { path: { id: 123 } },
})
// TypeScriptは`data`がどのような形かを正確に把握している
console.log(data?.email)
パスのタイプミス、誤ったパラメータ型、一致しないレスポンス形状はすべてコンパイルエラーになります。最小限のランタイムオーバーヘッドと小さなランタイム依存関係(openapi-fetch)のみです。
アプローチ2: 完全なクライアント生成のためのOrval
Orvalは型付きAPI関数を生成し、重要なことに、仕様から直接TanStack Queryフックを出力できます。これは、データ取得ロジックを自動的に処理したい場合に便利です。
npm install orval --save-dev
orval.config.tsを設定して仕様を指定し、出力モード(fetch、axios、またはreact-query)を選択します。OrvalはuseGetUsers()のような関数を生成し、完全な型安全性が組み込まれています。
このアプローチは生成コードが増えますが、大規模なAPIの場合、ボイラープレートを大幅に削減します。
型の同期を保つ
OpenAPI TypeScriptクライアント生成の真の価値は、一貫して再生成する場合にのみ維持されます。開発ワークフローに生成ステップを追加してください:
{
"scripts": {
"generate:api": "openapi-typescript ./openapi.yaml -o ./src/api/types.ts"
}
}
CIで実行するか、ローカルで仕様の変更を監視します。生成されたファイルをコミットするチームもあれば、ビルドごとに再生成するチームもあります。どちらでも機能します—自動化することが重要です。
まだ対処が必要なこと
生成された型は通常、コンパイル時の安全性のみを提供します。ランタイムバリデーションにはZodなどの追加ツールまたはジェネレータープラグインが必要です。重要なエンドポイントでは、生成された型とZodを組み合わせて、実行時にレスポンスを検証し、UIに到達する前にバックエンドのバグをキャッチします。
まとめ
OpenAPI TypeScriptコード生成は、フロントエンドコードベースに対して行える最も効果的な改善の1つです。軽量なセットアップにはopenapi-typescriptとopenapi-fetchを選択し、生成されたクエリフックが必要な場合はOrvalを選択してください。いずれにしても、手動で型を記述することをやめ、レスポンス形状を推測することをやめ、コンパイラに設計された仕事をさせることができます。
よくある質問
はい。openapi-typescriptはOpenAPI 3.0と3.1の両方の仕様をサポートしていますが、一部のスキーマ機能は特定のジェネレーターと仕様でテストが必要な場合があります。仕様バージョンをアップグレードした後は、常に生成された出力を検証してください。
もちろんです。生成された型はコンパイル時の安全性を提供し、Zodは実行時にデータを検証します。生成された型を反映するZodスキーマを定義し、APIレスポンスをそれらを通してパースできます。これにより、コンパイラが検出できないバックエンドが予期しないデータを返すケースをキャッチします。
どちらのアプローチも機能します。生成されたファイルをコミットすると、ビルドが高速になり、プルリクエストで型の変更をレビューできます。ビルドごとに再生成すると、型が常に最新であることが保証されますが、ビルドステップの依存関係が追加されます。チームのワークフローとCIセットアップに合うものを選択してください。
OrvalはTypeScript専用に構築されており、最小限の設定でTanStack Queryフック、Axiosクライアント、またはfetch関数を出力します。OpenAPI Generatorは多くの言語をサポートしていますが、より冗長なTypeScript出力を生成します。フロントエンド中心のチームにとって、Orvalは通常、クリーンで慣用的なコードを得るためのカスタマイズが少なくて済みます。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.