Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TypeSpecで実現する辛くないOpenAPIスキーマ駆動開発

 TypeSpecで実現する辛くないOpenAPIスキーマ駆動開発

2025/08/20 Mita.ts #7

Avatar for ふくすけ

ふくすけ

August 20, 2025
Tweet

More Decks by ふくすけ

Other Decks in Programming

Transcript

  1. 自己紹介 ふくすけ (@tonegawa07) スタークス株式会社 仕事: Engineer Ruby on Rails, TypeScript(Node.js)

    データ基盤 (BigQuery) 趣味: サッカー観戦 (Jサポ) ひとこと: 社内勉強会やテックブログの運営してます 2025/08/20 | Mita.ts 2
  2. OpenAPIとは REST APIを記述するための標準フォーマット openapi: 3.0.0 paths: /users: get: summary: ユーザー一覧取得

    responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/User' 2025/08/20 | Mita.ts 7
  3. 技術スタック バックエンド TypeScript Express openapi-typescript - OpenAPIから型定義を生成 フロントエンド TypeScript React

    openapi-typescript - OpenAPIから型定義を生成 openapi-fetch - 型安全なAPIクライアント 2025/08/20 | Mita.ts 10
  4. openapi-typescriptで型生成 npx openapi-typescript ./path/to/my/schema.yaml -o ./path/to/my/schema.d.ts import type { paths,

    components } from "./my-openapi-3-schema"; // Schema Obj type MyType = components["schemas"]["MyType"]; // Path params type EndpointParams = paths["/my/endpoint"]["parameters"]; // Response obj type SuccessResponse = paths["/my/endpoint"]["get"]["responses"][200]["content"]["application/json"]["schema"]; type ErrorResponse = paths["/my/endpoint"]["get"]["responses"][500]["content"]["application/json"]["schema"]; https://openapi-ts.dev/introduction 2025/08/20 | Mita.ts 12
  5. Express Request型の拡張 型安全なリクエストハンドラー import type { Request, Response } from

    "express"; import type { paths } from "./my-openapi-3-schema"; // OpenAPI のpaths から直接型を取得 type UpdateUserBody = paths["/users/{id}"]["patch"]["requestBody"]["content"]["application/json"]; type UpdateUserPath = paths["/users/{id}"]["patch"]["parameters"]["path"]; type UpdateUserQuery = paths["/users/{id}"]["patch"]["parameters"]["query"]; // Express Request 型に適用 app.patch("/users/:id", async ( req: Request<UpdateUserPath, unknown, UpdateUserBody, UpdateUserQuery>, res: Response ) => { const { id } = req.params; // 型安全! const { name } = req.body; // 型安全! const { sort } = req.query; // 型安全! }); 2025/08/20 | Mita.ts 13
  6. openapi-fetchで型安全なAPI呼び出し (GET) import createClient from "openapi-fetch"; import type { paths

    } from "./my-openapi-3-schema"; const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" }); const { data, // only present if 2XX response error, // only present if 4XX or 5XX response } = await client.GET("/blogposts/{post_id}", { params: { path: { post_id: "123" }, }, }); https://openapi-ts.dev/openapi-fetch/ 2025/08/20 | Mita.ts 14
  7. openapi-fetchで型安全なAPI呼び出し (PUT) import createClient from "openapi-fetch"; import type { paths

    } from "./my-openapi-3-schema"; const client = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" }); await client.PUT("/blogposts", { body: { title: "My New Post", }, }); https://openapi-ts.dev/openapi-fetch/ 2025/08/20 | Mita.ts 15
  8. openapi.yamlの記述例 components: schemas: User: type: object required: - id -

    email properties: id: type: string format: uuid email: type: string format: email 2025/08/20 | Mita.ts 17
  9. TypeSpecとは API-First for developers With TypeSpec, remove the handwritten files

    that slow you down, and generate standards-compliant API schemas in seconds. https://typespec.io/ TypeScriptライクな構文で、既存の知識をそのまま活用可能 TypeSpecから様々な形式に出力、それぞれのエコシステムと統合 マルチプロトコルサポート OpenAPI, JSON Schema, Protobuf 2025/08/20 | Mita.ts 20
  10. TypeSpecからOpenAPIへ TypeSpecファイル (.tspファイル) からopenapi.yamlを生成 # TypeSpec ファイル (main.tsp) をコンパイル tsp

    compile . # openapi.yaml が生成される openapi.yamlの直接編集から解放 2025/08/20 | Mita.ts 22
  11. TypeSpecのセットアップ # TypeSpec のインストール npm install -g @typespec/compiler # プロジェクトの初期化

    mkdir my-typespec-project cd my-typespec-project tsp init # コンパイル tsp compile . # src/main.tsp をコンパイルして dist ディレクトリにopenapi.yaml を生成 tsp compile ./src --output-dir ./dist https://typespec.io/docs/ 2025/08/20 | Mita.ts 23
  12. import "@typespec/http"; using Http; model User { id: string; email:

    string; address?: Address; } model Address { street: string; city: string; } @route("/users") interface Users { list(@query filter?: string): User[]; create(@body user: User): User; read(@path id: string): User; } 2025/08/20 | Mita.ts 24
  13. モデルとルートの分割 ファイル分割で可読性向上 src/ ├── main.tsp # エントリーポイント ├── models/ #

    データモデル定義 │ └── user.tsp └── routes/ # API ルート定義 └── users.tsp メリット 責務の分離が明確 可読性の向上 2025/08/20 | Mita.ts 25
  14. 分割例: モデル定義 models/user.tsp model User { id: string; email: string;

    address?: Address; } model Address { street: string; city: string; } 2025/08/20 | Mita.ts 26
  15. 分割例: ルート定義 routes/users.tsp import "@typespec/http"; import "../models/user.tsp"; using Http; @route("/users")

    interface Users { list(@query filter?: string): User[]; create(@body user: User): User; read(@path id: string): User; } 2025/08/20 | Mita.ts 27
  16. CIでopenapi.yaml自動生成 GitHub Actionsによる自動化フロー CI 処理 変更あり 変更なし 差分あり 差分なし PR

    作成/ 更新 TypeSpec 変更チェック コンパイル実行 OpenAPI 差分チェック 自動コミット PR に反映 終了 2025/08/20 | Mita.ts 29
  17. 開発フロー TypeSpecを用いたスキーマ駆動開発 API 設計 TypeSpec でAPI 仕様を定義 openapi.yaml 生成 openapi-typescript

    で型生成 バックエンド(Express Reques フロントエンド(openapi-fetc 2025/08/20 | Mita.ts 30