Sequelizeモデルから自動でSwagger型定義を生成する方法



# Sequelizeモデルから自動でSwagger型定義を生成する方法

## はじめに

REST APIのドキュメント作成には、Swaggerが広く使用されています。この記事では、Sequelizeモデルから自動的にSwagger用の型定義を生成し、APIドキュメントを自動更新する方法を解説します。

## プロジェクト構成例

````
.
└── backend/
    └── src/
        ├── models/          # Sequelizeモデル
        │   ├── index.ts
        │   └── User.ts
        ├── scripts/
        │   └── generateSwagger.ts  # Swagger生成スクリプト
        ├── swagger-schemas.ts      # 生成される型定義
        └── server.ts              # Express設定
````


## 実装手順

### 1. Sequelizeの定義からSwagger型定義を生成する関数の定義

````typescript:src/scripts/generateSwagger.ts
import fs from 'fs';
import path from 'path';
import { Model, ModelStatic, DataTypes } from 'sequelize';

// Sequelizeの型をSwagger型に変換する関数
function getSwaggerType(sequelizeType: any): any {
  const type = sequelizeType.toString().toLowerCase();

  if (sequelizeType instanceof DataTypes.ENUM) {
    return {
      type: 'string',
      enum: (sequelizeType as any).values,
    };
  }

  if (type.includes('uuid')) {
    return {
      type: 'string',
      format: 'uuid',
    };
  }

  if (type.includes('int') || type.includes('float')) {
    return { type: 'integer' };
  }

  if (type.includes('boolean')) {
    return { type: 'boolean' };
  }

  if (type.includes('date')) {
    return {
      type: 'string',
      format: 'date-time',
    };
  }

  return { type: 'string' };
}

// モデルからSwaggerスキーマを生成
function generateSwaggerSchema(model: ModelStatic<Model>) {
  const attributes = model.getAttributes();
  const properties: Record<string, any> = {};
  const required: string[] = [];

  for (const [key, attribute] of Object.entries(attributes)) {
    properties[key] = getSwaggerType(attribute.type);
    if (!attribute.allowNull) {
      required.push(key);
    }
  }

  return {
    type: 'object',
    properties,
    required: required.length > 0 ? required : undefined,
  };
}
````


### 2. 共通レスポンススキーマの定義

Sequelizeの型定義とは関係ないが、よく使うレスポンスがあれば、定義する

````typescript:src/scripts/generateSwagger.ts
const commonSchemas = {
  SuccessResponse: {
    type: 'object',
    properties: {
      message: {
        type: 'string',
        description: '処理成功時のメッセージ',
      },
    },
    required: ['message'],
    example: {
      message: '正常に処理が完了しました',
    },
  },
  Error400Response: {
    type: 'object',
    properties: {
      error: {
        type: 'string',
        description: 'エラーメッセージ',
      },
    },
    required: ['error'],
    example: {
      error: 'リクエストが不正です',
    },
  },
  // ... その他のエラーレスポンス
};
````


### 3. スキーマ生成の実行

````typescript:src/scripts/generateSwagger.ts
(async function () {
  let output = `// This file is auto-generated. DO NOT EDIT.
export const swaggerSchemas = {
  components: {
    schemas: {
      ...${JSON.stringify(commonSchemas, null, 6)},`;

  // モデルファイルを読み込んでスキーマを生成
  fs.readdirSync(modelsDir)
    .filter((file) => file.endsWith('.ts') && file !== 'index.ts')
    .forEach((file) => {
      const modelModule = require(path.join(modelsDir, file));
      const model = modelModule.default;
      if (model && model.prototype instanceof Model) {
        const schema = generateSwaggerSchema(model);
        output += `
      ${model.name}: ${JSON.stringify(schema, null, 6)},`;
      }
    });

  output += `
    }
  }
};`;

  fs.writeFileSync(outputPath, output);
  console.log('✨ Swagger schemas generated successfully!');
})();
````

### 4. スキーマ出力の例
````typescript:swagger-schemas.ts
// This file is auto-generated. DO NOT EDIT.
/* eslint-disable */
export const swaggerSchemas = {
  components: {
    schemas: {
      ...{
        SuccessResponse: {
          type: 'object',
          properties: {
            message: {
              type: 'string',
              description: '処理成功時のメッセージ',
            },
          },
          required: ['message'],
          example: {
            message: '正常に処理が完了しました',
          },
        },
        Error400Response: {
          type: 'object',
          properties: {
            error: {
              type: 'string',
              description: 'エラーメッセージ',
            },
          },
          required: ['error'],
          example: {
            error: 'リクエストが不正です',
          },
        },
      },
      User: {
        type: 'object',
        properties: {
          id: {
            type: 'string',
            format: 'uuid',
          },
          username: {
            type: 'string',
          },
          createdAt: {
            type: 'string',
          },
          updatedAt: {
            type: 'string',
          },
        },
        required: ['id', 'username', 'createdAt', 'updatedAt'],
      },
    },
  },
};


````

### 5. ExpressでのSchema利用例
Swaggerのoption設定で、先ほど作成したSchemaを反映させます

````typescript:src/server.ts
import swaggerUi from 'swagger-ui-express';
import swaggerJsdoc from 'swagger-jsdoc';
import { swaggerSchemas } from './swagger-schemas';

// Swagger設定
const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'API Documentation',
      version: '1.0.0',
    },
    ...swaggerSchemas,
  },
  apis: ['./src/routes/*.ts'],
};

const swaggerSpec = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
````


### 6. ルーターでの使用例

各プロパティを書くことなく、$refという形で型定義を記載することができます

````typescript:src/routes/prototype.ts
/**
 * @swagger
 * /api/prototypes:
 *   post:
 *     summary: プロトタイプ作成
 *     requestBody:
 *       content:
 *         application/json:
 *           schema:
 *             $ref: '#/components/schemas/Prototype'
 *     responses:
 *       '201':
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Prototype'
 *       '400':
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Error400Response'
 */
router.post('/', async (req, res) => {
  // 実装...
});
````


### 7. Swagger UI
レスポンス例にも適当な値が出力されるため、レスポンスの雰囲気を掴むには十分です

![スクリーンショット 2024-12-30 20.10.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518208/b0a64cab-b1ae-38b3-51f0-eb7004a61cb9.png)

## まとめ

Sequelizeモデルからの自動Swagger生成により：
- APIドキュメントの保守性向上
- モデル定義とAPI仕様の一貫性確保
- 開発効率の改善

が実現できます。

## 参考資料
- [Swagger UI Express](https://www.npmjs.com/package/swagger-ui-express)
- [Swagger JSDoc](https://www.npmjs.com/package/swagger-jsdoc)
- [OpenAPI Specification](https://swagger.io/specification/)