adopting-generated-api-types について
PostHog のフロントエンド開発者向けスキルです。手書きの API 呼び出しと型を、生成された TypeScript の関数や型に移行する手順を案内します。互換性の確認と検証方法も扱います。
主な機能
- 013 種類の手書き API 呼び出しの特定
- 02生成関数と型の検索
- 03互換性確認と呼び出しの置換
- 04旧型の削除と検証
活用シーン
- 01旧式 API リクエストの置換
- 02生成されたレスポンス型の採用
- 03シリアライザー変更後のフロントエンド整理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ posthog/ posthog --skill adopting-generated-api-typesインストール後、Claude Code で/adopting-generated-api-typesを入力して読み込まれたか確認します。
同梱ファイルreferences/migration-patterns.mdreferences/type-compatibility.md
詳細
生成された API 型の採用
概要
PostHog は、OpenAPI パイプラインを通じて Django シリアライザーから TypeScript API クライアント関数と型を生成します。
Django serializer → drf-spectacular → OpenAPI JSON → Orval → TypeScript (api.ts + api.schemas.ts + api.zod.ts)
生成されたファイルは次の場所にあります。
- コア:
frontend/src/generated/core/api.ts、api.schemas.ts、api.zod.ts - プロダクト:
products/<product>/frontend/generated/api.ts、api.schemas.ts、api.zod.ts
生成された型は Api サフィックスを使用します (DashboardApi、SurveyApi)。手書きの型は決してこれを使用しません。
このスキルは、手動の API 呼び出しと手書きの型を、生成された同等のものに置き換える手順を案内します。
移行すべき 3 つの手動パターン
レガシーの frontend/src/lib/api.ts (約 6000 行) には 3 つのレイヤーがあり、すべてが移行対象です。
1. 高レベルオブジェクト API (最も一般的)
api オブジェクト上のドメイン固有の便利メソッドです。
api.surveys.get(id)
api.surveys.create(data)
api.dashboards.list()
api.cohorts.update(id, data)
api.actions.create(data)
これらは最も広く使用されているパターンです。すべてのエンティティには、CRUD とカスタムメソッド (例: api.surveys.getResponsesCount()、api.dashboards.streamTiles()) を備えた独自の名前空間があります。
2. 手動 URL を指定する生の HTTP メソッド
api.get<SomeType>(`api/projects/${id}/surveys/`)
api.create<SomeType>(`api/projects/${id}/surveys/`, data)
api.update<SomeType>(url, data)
api.put<SomeType>(url, data)
api.delete(url)
3. ApiRequest ビルダー (流暢な URL 構築)
const url = new ApiRequest().surveys().assembleFullUrl()
const response = await api.get(url)
// or directly:
await new ApiRequest().survey(surveyId).withAction('summarize_responses').create({ data })
3 つのパターンはすべて、可能な場合は生成された関数に置き換えるべきです。
いつ使用するか
api.<entity>.<method>()(例:api.surveys.get()) を呼び出すファイルを編集するときapi.get<T>(...)、api.create<T>(...)などを呼び出すファイルを編集するときnew ApiRequest()を使用して URL を構築するファイルを編集するとき- API レスポンスの形状のために
~/typesから手書きのインターフェースをインポートするファイルを編集するとき - バックエンドのシリアライザー改善後にフロントエンドコードをクリーンアップするとき
ステップバイステップのワークフロー
1. 手動呼び出しが何をするかを特定する
既存の呼び出しを見て、次を抽出します。
- HTTP メソッド — GET、POST、PUT、PATCH、DELETE
- エンティティとアクション — どのリソース、どの操作か
- 型パラメーター — レスポンスに使用される手書きの型
2. 生成された同等のものを探す
生成された関数名は {resource}{Action} の命名規則に従います。
surveysList — GET /api/projects/{id}/surveys/
surveysCreate — POST /api/projects/{id}/surveys/
surveysRetrieve — GET /api/projects/{id}/surveys/{id}/
surveysPartialUpdate — PATCH /api/projects/{id}/surveys/{id}/
surveysDestroy — DELETE /api/projects/{id}/surveys/{id}/
検索する場所:
- コアエンドポイント:
frontend/src/generated/core/api.ts - プロダクトエンドポイント:
products/<product>/frontend/generated/api.ts
検索戦略:
- 生成された
api.tsファイル内でエンティティ名を grep する get*Urlヘルパー関数で検索する — すべての生成された関数には、その上に URL ビルダーがありますapi.schemas.tsでApiサフィックス付きの型名を検索する
生成された関数が存在しない場合、バックエンドエンドポイントに @extend_schema または @validated_request が欠けている可能性があります。まず improving-drf-endpoints スキルを使用してバックエンドを修正し、その後 hogli build:openapi を実行します。
カスタムアクション (api.surveys.summarize_responses() など) は、バックエンドの @action に @extend_schema が欠けている場合、生成された同等のものを持たないことがあります。まず生成されたファイルを確認し、存在しない場合はバックエンドを修正します。
3. 型の互換性を確認する
手書きの型と生成された Api 型を比較します。主な違い:
readonly修飾子 — 生成された型は読み取り専用フィールドをマークします- オプション vs 必須 — 生成された型は
required=を正確に反映します - Null 許容性 —
null型は明示的です - 追加フィールド — 生成された型には、手書きの型が省略しているフィールドが含まれる場合があります
詳細は type-compatibility.md を参照してください。
4. 呼び出しを置き換える
以下を網羅した詳細な before/after の例については、migration-patterns.md を参照してください。
- 高レベルオブジェクト API (
api.surveys.get()→surveysRetrieve()) - 生の HTTP メソッド (
api.get<T>(url)→ 生成された関数) - ApiRequest ビルダー → 生成された関数
- ページネーションされたリスト呼び出し
- リクエストボディを伴う作成/更新
- 削除呼び出し
- Kea ロジックのローダーとリスナー
- abort シグナルを伴う呼び出し
5. 使用箇所で型を置き換える
下流の参照を手書きの型から生成された型に更新します。
// Before
function renderSurvey(survey: Survey): JSX.Element { ... }
// After
function renderSurvey(survey: SurveyApi): JSX.Element { ... }
6. 不要になった型をクリーンアップする
手書きの型のすべての使用箇所を移行した後:
~/typesまたはローカルファイルから型定義を削除します- 未使用のインポートを削除します
pnpm --filter=@posthog/frontend typescript:checkを実行して、破損がないことを確認します
判断ガイド
| シナリオ | 対応 |
|---|---|
| 生成された関数が存在する | 手動呼び出しを生成された関数に置き換える |
| 生成された型は存在するが、関数は存在しない | 手動呼び出しのジェネリックパラメータとして生成された型を使用し、@extend_schema を追加するフォローアップを起票する |
| どちらも存在しない | 手動パターンを維持し、先にバックエンドのシリアライザー/ビューセットを修正する |
| 生成された等価物がないカスタムアクション | api.<entity>.<method>() 呼び出しを維持し、先にバックエンドの @action アノテーションを修正する |
| 生成された型が手書きのものと異なる形状を持つ | 呼び出し箇所を生成された形状に適応させる — シリアライザーが信頼できる唯一の情報源です |
| コードがレスポンスオブジェクトを変更する | ローカルの可変コピーを使用します: const mutable = { ...response } を作成し、それを変更します |
| 読み取り用と書き込み用の両方の型が必要 | 読み取りには FooApi を使用し、書き込み用の型は Parameters<typeof fooCreate>[1] 経由で導出するか、PatchedFooApi を使用します |
インポートの規約
// Core generated functions — import from api.ts
import { domainsList, domainsCreate, domainsRetrieve } from '~/generated/core/api'
// Core generated types — import type from api.schemas.ts
import type { OrganizationDomainApi } from '~/generated/core/api.schemas'
// Core generated Zod schemas — import from api.zod.ts
import { DomainsCreateBody } from '~/generated/core/api.zod'
// Product generated functions — NO tilde prefix, use 'products/' path
import { surveysList, surveysRetrieve } from 'products/surveys/frontend/generated/api'
import type { SurveyApi } from 'products/surveys/frontend/generated/api.schemas'
import { SurveysCreateBody } from 'products/surveys/frontend/generated/api.zod'
// Within a product, relative imports also work
import { logsAlertsCreate } from '../generated/api'
import type { LogsAlertConfigurationApi } from '../generated/api.schemas'
import { LogsAlertsCreateBody } from '../generated/api.zod'
パスのルール:
- コア:
~/generated/core/...(チルダプレフィックス) - 外部からのプロダクト:
products/<product>/frontend/generated/...(チルダなし) - 内部からのプロダクト: 相対パス
../generated/...または./generated/...
適切なツリーシェイキングを有効にするために、型には import type を使用します。
生成された関数の内部動作
生成された関数は、api-orval-mutator.ts を介して同じ api モジュールをラップします:
surveysList(projectId, params)
→ apiMutator(url, { method: 'GET' })
→ api.get(url)
生成された関数に切り替えても HTTP の動作は変わりません — 同じ Cookie、同じ CSRF、同じエラーハンドリングです。唯一の違いは型安全性と URL の構築です。
移行の検証
- TypeScript チェック:
pnpm --filter=@posthog/frontend typescript:check - 残った手動型を grep で検索: コードベース全体で古い型名を検索します
- 関連するテストを実行:
hogli test <test_file>
関連
- バックエンド側: 不適切な型を生成するシリアライザーを修正するには
improving-drf-endpointsを使用します - 型システムのドキュメント:
docs/published/handbook/engineering/type-system.md - API ミューテーター:
frontend/src/lib/api-orval-mutator.ts - 型を再生成:
hogli build:openapi