2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

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型定義を生成する関数の定義

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の型定義とは関係ないが、よく使うレスポンスがあれば、定義する

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. スキーマ生成の実行

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. スキーマ出力の例

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を反映させます

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という形で型定義を記載することができます

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

まとめ

Sequelizeモデルからの自動Swagger生成により:

  • APIドキュメントの保守性向上
  • モデル定義とAPI仕様の一貫性確保
  • 開発効率の改善

が実現できます。

参考資料

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?