openapi-codegen について
OpenAPI ドキュメントからの型や API クライアントの生成、tRPC ルーターの OpenAPI 出力を扱う、TypeScript 開発者向けのコーディングスキルです。仕様の検証、自動生成、生成時の問題への対処も説明します。
主な機能
- 01OpenAPI から TypeScript 型を生成
- 02fetch または axios の API クライアント生成
- 03テンプレートによる出力のカスタマイズ
- 04tRPC ルーターを OpenAPI に出力
- 05生成の検証と自動化
活用シーン
- 01TypeScript プロジェクトに型付き API 契約を追加
- 02ローカルまたはリモートの仕様からクライアントを生成
- 03OpenAPI ドキュメントを通じて tRPC エンドポイントを公開
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill openapi-codegenインストール後、Claude Code で/openapi-codegenを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
quick-ref/openapi-generator.mdopenapi-typescript.mdswagger-typescript-api.mdtrpc-openapi.md詳細
OpenAPI コード生成のコア知識
深い知識:包括的なドキュメントについては、technology:
openapi-codegenを指定してmcp__documentation__fetch_docsを使用してください。
openapi-typescript(型のみ)
OpenAPI スキーマから TypeScript の型を生成します。軽量で、実行時の処理はありません。
# Installation
npm install -D openapi-typescript
# Generate types
npx openapi-typescript ./openapi.yaml -o ./src/types/api.ts
npx openapi-typescript https://api.example.com/openapi.json -o ./src/types/api.ts
生成された型の使い方
import type { paths, components } from './types/api';
// Schema types
type User = components['schemas']['User'];
type CreateUserDto = components['schemas']['CreateUserDto'];
// Request/Response types
type CreateUserRequest = paths['/users']['post']['requestBody']['content']['application/json'];
type UserResponse = paths['/users/{id}']['get']['responses']['200']['content']['application/json'];
type UsersListResponse = paths['/users']['get']['responses']['200']['content']['application/json'];
// Path parameters
type UserPathParams = paths['/users/{id}']['get']['parameters']['path'];
openapi-fetch との併用
import createClient from 'openapi-fetch';
import type { paths } from './types/api';
const client = createClient<paths>({
baseUrl: 'https://api.example.com',
});
// Fully typed requests
const { data, error } = await client.GET('/users/{id}', {
params: { path: { id: '123' } },
});
const { data } = await client.POST('/users', {
body: { name: 'John', email: '[email protected]' },
});
// Query parameters
const { data } = await client.GET('/users', {
params: { query: { status: 'active', page: 1 } },
});
openapi-generator-cli(フルクライアント)
fetch/axios の実装を含む、完全な API クライアントを生成します。
# Installation
npm install -D @openapitools/openapi-generator-cli
# Generate TypeScript Fetch client
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-fetch \
-o ./src/api-client
# Generate TypeScript Axios client
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-axios \
-o ./src/api-client
設定ファイル
# openapitools.json
{
"$schema": "https://raw.githubusercontent.com/OpenAPITools/openapi-generator-cli/master/apps/generator-cli/src/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.0.0",
"generators": {
"typescript-client": {
"generatorName": "typescript-fetch",
"output": "#{cwd}/src/api-client",
"inputSpec": "#{cwd}/openapi.yaml",
"additionalProperties": {
"supportsES6": true,
"npmName": "@myorg/api-client",
"typescriptThreePlus": true,
"withInterfaces": true
}
}
}
}
}
生成されたクライアントの使い方
import { Configuration, UsersApi } from './api-client';
const config = new Configuration({
basePath: 'https://api.example.com',
accessToken: () => localStorage.getItem('token') || '',
});
const usersApi = new UsersApi(config);
// Typed API calls
const users = await usersApi.listUsers({ status: 'active' });
const user = await usersApi.getUserById({ id: '123' });
const newUser = await usersApi.createUser({
createUserDto: { name: 'John', email: '[email protected]' },
});
swagger-typescript-api
テンプレートに対応した、高速でカスタマイズ可能なジェネレーターです。
# Installation
npm install -D swagger-typescript-api
# Generate
npx swagger-typescript-api -p ./openapi.yaml -o ./src/api -n api.ts
設定
npx swagger-typescript-api \
-p ./openapi.yaml \
-o ./src/api \
-n api.ts \
--axios \ # Use axios instead of fetch
--modular \ # Separate files per tag
--route-types \ # Generate route types
--extract-request-body \ # Extract request body types
--extract-response-body # Extract response body types
生成されたクライアントの使い方
import { Api } from './api/api';
const api = new Api({
baseUrl: 'https://api.example.com',
securityWorker: () => ({
headers: { Authorization: `Bearer ${getToken()}` },
}),
});
// Typed API calls
const users = await api.users.usersList({ status: 'active' });
const user = await api.users.usersDetail('123');
const newUser = await api.users.usersCreate({
name: 'John',
email: '[email protected]',
});
trpc-openapi(tRPC を OpenAPI としてエクスポート)
tRPC ルーターから OpenAPI 仕様を生成します。外部 API 利用者に役立ちます。
npm install trpc-openapi
OpenAPI エンドポイントの定義
import { initTRPC } from '@trpc/server';
import { OpenApiMeta } from 'trpc-openapi';
import { z } from 'zod';
const t = initTRPC.meta<OpenApiMeta>().create();
export const appRouter = t.router({
getUser: t.procedure
.meta({
openapi: {
method: 'GET',
path: '/users/{id}',
tags: ['users'],
summary: 'Get user by ID',
},
})
.input(z.object({ id: z.string() }))
.output(z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}))
.query(({ input }) => getUserById(input.id)),
createUser: t.procedure
.meta({
openapi: {
method: 'POST',
path: '/users',
tags: ['users'],
summary: 'Create a new user',
},
})
.input(z.object({
name: z.string(),
email: z.string().email(),
}))
.output(z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}))
.mutation(({ input }) => createUser(input)),
});
OpenAPI ドキュメントの生成
import { generateOpenApiDocument } from 'trpc-openapi';
import { appRouter } from './router';
const openApiDocument = generateOpenApiDocument(appRouter, {
title: 'My API',
version: '1.0.0',
baseUrl: 'https://api.example.com',
});
// Save to file
import fs from 'fs';
fs.writeFileSync('./openapi.json', JSON.stringify(openApiDocument, null, 2));
REST ハンドラー
import { createOpenApiNextHandler } from 'trpc-openapi';
import { appRouter } from './router';
// Next.js API route: pages/api/[...trpc].ts
export default createOpenApiNextHandler({
router: appRouter,
createContext: () => ({}),
});
本番環境への対応
CI/CD との統合
# .github/workflows/generate-client.yml
name: Generate API Client
on:
push:
paths:
- 'openapi.yaml'
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Generate types
run: npx openapi-typescript ./openapi.yaml -o ./src/types/api.ts
- name: Check for changes
id: check
run: |
if git diff --quiet src/types/api.ts; then
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Commit and push
if: steps.check.outputs.changed == 'true'
run: |
git config --local user.email "[email protected]"
git config --local user.name "API Generator Bot"
git add src/types/api.ts
git commit -m "chore: regenerate API types"
git push
パッケージスクリプト
{
"scripts": {
"generate:types": "openapi-typescript ./openapi.yaml -o ./src/types/api.ts",
"generate:client": "openapi-generator-cli generate -c openapitools.json",
"generate:all": "npm run generate:types && npm run generate:client",
"precommit": "npm run generate:types && git add src/types/api.ts"
}
}
ウォッチモードでの開発
# openapi-typescript with watch
npx openapi-typescript ./openapi.yaml -o ./src/types/api.ts --watch
# Or use nodemon
npx nodemon --watch openapi.yaml --exec "npx openapi-typescript ./openapi.yaml -o ./src/types/api.ts"
生成前の検証
# Validate spec first
npx @redocly/cli lint openapi.yaml
# Then generate
npx openapi-typescript ./openapi.yaml -o ./src/types/api.ts
メトリクスの監視
| メトリクス | 目標 |
|---|---|
| 生成された型のカバレッジ | エンドポイントの 100% |
| 生成を含むビルド時間 | < 30s |
| 生成後の型エラー | 0 |
| 仕様の検証エラー | 0 |
チェックリスト
- CI で OpenAPI 仕様を検証する
- 仕様変更時に型を自動生成する
- 生成コードをコミットするか、gitignore に追加する
- ジェネレーター CLI のバージョンを固定する
- カスタムテンプレートを文書化する
- API クライアントの初期化方法を文書化する
- エラー処理のパターンを文書化する
- クライアントの認証設定を行う
- 破壊的変更を検出する
- 生成コードをテストする
このスキルを使用しない場合
- OpenAPI 仕様を記述する場合(
openapiスキルを使用) - GraphQL の型を生成する場合(
graphql-codegenスキルを使用) - tRPC の型安全な API を使用する場合(
trpcスキルを使用) - API クライアントを手動で実装する場合
- 手動での型定義で十分なシンプルな API の場合
アンチパターン
| アンチパターン | 問題点 | 解決策 |
|---|---|---|
| 生成コードを git にコミットする | マージ競合、コードの陳腐化 | .gitignore に追加し、CI/ビルド時に生成する |
| ジェネレーター CLI のバージョンを管理しない | 出力が一貫しない | package.json でジェネレーターのバージョンを固定する |
| 生成されたファイルを手動で編集する | 再生成時に変更が失われる | 生成コードを拡張またはラップする |
| 生成前に検証しない | 無効な型が生成される | まず @redocly/cli で仕様を検証する |
| チーム内で異なるジェネレーターを使用する | 型に一貫性がなくなる | ジェネレーターを 1 つに統一する |
| キャッシュせずにリモート仕様から生成する | ビルドが遅くなり、ネットワークに依存する | 仕様をローカルにキャッシュするか、スキーマレジストリを使用する |
| 仕様変更時に更新しない | 型と実行時の動作が一致しなくなる | 仕様の更新時に CI で生成を実行する |
| 生成コードにエラー処理がない | エラー時の UX が悪くなる | 生成されたクライアントをエラー処理でラップする |
すばやいトラブルシューティング
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| 生成に失敗する | OpenAPI 仕様が無効 | @redocly/cli lint で検証する |
| 生成後に型エラーが発生する | 仕様が API と一致していない | 仕様が実際のレスポンスと一致するか確認する |
| 型が不足している | 仕様が不完全、または参照が壊れている | すべての $refs が解決されることを確認し、不足しているスキーマを追加する |
| 意図しない HTTP クライアントが生成される | ジェネレーターの設定が一致していない | -g フラグまたはジェネレーター設定を確認する |
| 循環参照エラーが発生する | スキーマが自身を参照している | discriminator を使用するか、スキーマをフラット化する |
| 生成に時間がかかる | 仕様ファイルが大きい | 仕様を分割するか、一部のみを生成する |
| 認証が機能しない | セキュリティスキームが設定されていない | securityWorker またはトークン設定を追加する |
| 「Cannot find module」エラーが発生する | 生成が完了していない | 生成エラーを確認し、再実行する |
| 型の競合が発生する | 複数のジェネレーターが実行されている | ジェネレーターを 1 つだけ使用し、他を削除する |
Reference Documentation
コメント
Markdown 対応