LESSON 30分

ストーリー

あなた
OpenAPIの仕様書を書いた。で、次はコードを書く…ですよね?

高橋アーキテクトが首を振った。

高橋アーキテクト
半分正解だ。OpenAPIの真の力は、仕様書からコードを自動生成できることにある
高橋アーキテクト
型定義やバリデーションも自動で?
高橋アーキテクト
そうだ。TypeScriptの型、APIクライアント、バリデーションミドルウェア、ドキュメント — すべて仕様書から生成できる。仕様と実装の乖離が起きない。これが”契約ファースト”のメリットだ

ドキュメント自動生成

Swagger UI

// OpenAPI仕様書からインタラクティブなAPIドキュメントを生成

// Express でSwagger UIを提供する例
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';

const swaggerDocument = YAML.load('./openapi.yaml');

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
  customCss: '.swagger-ui .topbar { display: none }',
  customSiteTitle: 'TaskFlow API Documentation',
}));

// → http://localhost:3000/api-docs でドキュメントが閲覧可能
// → 各エンドポイントを画面上でテスト実行できる

Redoc

// Redocはより美しいドキュメントUIを提供
// 静的HTMLとしても生成可能

// Express での設定
import redoc from 'redoc-express';

app.get('/docs', redoc({
  title: 'TaskFlow API',
  specUrl: '/openapi.yaml',
}));

// CLIで静的HTMLを生成
// npx @redocly/cli build-docs openapi.yaml -o docs/index.html

TypeScript型の自動生成

openapi-typescript

// OpenAPI仕様書からTypeScriptの型定義を生成

// インストールと実行
// npx openapi-typescript openapi.yaml -o src/types/api.ts

// 生成される型定義の例
// src/types/api.ts
export interface paths {
  "/api/v1/users": {
    get: {
      parameters: {
        query: {
          page?: number;
          perPage?: number;
        };
      };
      responses: {
        200: {
          content: {
            "application/json": {
              data: components["schemas"]["User"][];
              meta: components["schemas"]["PaginationMeta"];
            };
          };
        };
      };
    };
    post: {
      requestBody: {
        content: {
          "application/json": components["schemas"]["CreateUserRequest"];
        };
      };
      responses: {
        201: {
          content: {
            "application/json": {
              data: components["schemas"]["User"];
            };
          };
        };
      };
    };
  };
}

export interface components {
  schemas: {
    User: {
      id: string;
      name: string;
      email: string;
      role: "admin" | "member" | "viewer";
      createdAt: string;
    };
    CreateUserRequest: {
      name: string;
      email: string;
      password: string;
    };
    PaginationMeta: {
      totalCount: number;
      currentPage: number;
      perPage: number;
      totalPages: number;
    };
  };
}

型安全なAPIクライアント

// 生成された型を使って型安全なAPIクライアントを構築
import createClient from 'openapi-fetch';
import type { paths } from './types/api';

const client = createClient<paths>({
  baseUrl: 'https://api.taskflow.example.com/v1',
});

// 型安全なAPI呼び出し
async function getUsers() {
  const { data, error } = await client.GET('/api/v1/users', {
    params: {
      query: { page: 1, perPage: 20 },
    },
  });

  if (error) {
    console.error(error);
    return;
  }

  // data.data は User[] 型として推論される
  data.data.forEach(user => {
    console.log(user.name);  // 型安全
    // user.foo → コンパイルエラー(存在しないプロパティ)
  });
}

サーバーサイドのコード生成

バリデーションミドルウェア

// OpenAPI仕様書に基づくリクエストバリデーション
// express-openapi-validator パッケージを使用

import * as OpenApiValidator from 'express-openapi-validator';

app.use(
  OpenApiValidator.middleware({
    apiSpec: './openapi.yaml',
    validateRequests: true,   // リクエストのバリデーション
    validateResponses: true,  // レスポンスのバリデーション(開発時)
  })
);

// バリデーションエラーのハンドリング
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
  if (err.status === 400 || err.status === 422) {
    return res.status(err.status).json({
      error: {
        code: 'VALIDATION_ERROR',
        message: err.message,
        details: err.errors?.map((e: any) => ({
          field: e.path,
          message: e.message,
        })),
        traceId: req.id,
      }
    });
  }
  next(err);
});

// これにより、仕様書で定義した制約(minLength, pattern, enum等)が
// 自動的にバリデーションされる

コード生成ツールの比較

ツール用途言語
openapi-typescriptTypeScript型生成TypeScript
openapi-fetch型安全なfetchクライアントTypeScript
orvalクライアントコード生成TypeScript
openapi-generator多言語クライアント/サーバー生成多言語
express-openapi-validatorリクエスト/レスポンスバリデーションNode.js
@redocly/cliドキュメント生成、リンティング任意

OpenAPI リンティング

// Redocly CLIで仕様書の品質チェック
// npx @redocly/cli lint openapi.yaml

// チェック内容の例:
// - 未使用のスキーマ定義
// - operationIdの欠落
// - descriptionの欠落
// - レスポンスの定義漏れ
// - セキュリティスキームの設定忘れ

// .redocly.yaml で設定
// extends:
//   - recommended
// rules:
//   operation-description: error
//   operation-operationId: error
//   no-unused-components: warn

まとめ

ポイント内容
ドキュメント生成Swagger UI / Redoc でインタラクティブなドキュメント
TypeScript型生成openapi-typescript で仕様から型を生成
クライアント生成openapi-fetch で型安全なAPIクライアント
バリデーションexpress-openapi-validator で自動バリデーション
リンティング@redocly/cli で仕様書の品質チェック

チェックリスト

  • Swagger UI / Redoc によるドキュメント自動生成を理解した
  • OpenAPIからTypeScript型を生成する方法を把握した
  • 型安全なAPIクライアントの構築方法を理解した
  • リクエスト/レスポンスの自動バリデーションの仕組みを把握した

次のステップへ

コード生成とドキュメント自動化を学びました。

次のセクションでは、API仕様ファーストの開発フローを学びます。 仕様を先に書き、実装はその仕様に従うという開発手法です。


推定読了時間: 30分