REST APIを開発する際、正確で最新のAPI仕様書を維持することは重要ですが、手動でのドキュメント管理は非効率で整合性の問題が発生しやすくなります。NestJSでは、@nestjs/swaggerパッケージを使用することで、コードに記述したデコレータから自動的にOpenAPI仕様に準拠したインタラクティブなAPI仕様書を生成できます。本記事では、Swaggerの導入から各種デコレータの活用方法まで、実践的な手順を解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Node.js 20以上
npm 10以上
NestJS 11.x
@nestjs/swagger 11.x
TypeScript 5.x
OS Windows / macOS / Linux

事前に以下の準備を完了してください。

  • NestJSプロジェクトが作成済みであること
  • 基本的なController、Service、DTOの実装経験があること

OpenAPIとSwaggerの概要

OpenAPIは、RESTful APIを記述するための言語に依存しない標準仕様です。この仕様に基づいてAPI定義を記述することで、以下のメリットが得られます。

  • インタラクティブなAPIドキュメントの自動生成
  • クライアントSDKの自動生成
  • APIテストの自動化
  • チーム間でのAPI仕様の共有と合意形成

Swaggerは、OpenAPI仕様を扱うためのツール群の総称であり、Swagger UIはOpenAPI定義からインタラクティブなドキュメントを生成するツールです。NestJSでは@nestjs/swaggerパッケージを通じて、これらの機能をシームレスに統合できます。

@nestjs/swaggerパッケージのインストール

まず、プロジェクトに@nestjs/swaggerパッケージをインストールします。

1
npm install --save @nestjs/swagger

このパッケージには、Swagger UIのアセットとOpenAPIドキュメント生成に必要なすべての機能が含まれています。

SwaggerModuleの基本設定

main.tsファイルでSwaggerModuleを設定します。DocumentBuilderクラスを使用してAPIドキュメントの基本情報を定義し、SwaggerModuleでドキュメントを生成・公開します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Task Management API')
    .setDescription('タスク管理アプリケーションのREST API仕様書')
    .setVersion('1.0')
    .addTag('tasks')
    .addTag('users')
    .build();

  const documentFactory = () => SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api-docs', app, documentFactory);

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

DocumentBuilderの主要なメソッドを以下に示します。

メソッド 説明
setTitle() APIドキュメントのタイトルを設定
setDescription() APIの説明文を設定
setVersion() APIのバージョンを設定
addTag() エンドポイントをグループ化するタグを追加
addBearerAuth() Bearer認証のセキュリティスキームを追加
setContact() 連絡先情報を設定
setLicense() ライセンス情報を設定

アプリケーションを起動し、http://localhost:3000/api-docsにアクセスすると、Swagger UIが表示されます。

1
npm run start:dev

Swagger UIとAPI定義ファイルのエンドポイント

SwaggerModule.setup()の第1引数で指定したパスに基づいて、以下のエンドポイントが自動的に公開されます。

エンドポイント 内容
/api-docs Swagger UIのインタラクティブなドキュメント画面
/api-docs-json OpenAPI仕様のJSONファイル
/api-docs-yaml OpenAPI仕様のYAMLファイル

JSONやYAMLファイルは、外部ツールでのクライアント生成やAPI検証に活用できます。

@ApiTags()によるエンドポイントのグループ化

@ApiTags()デコレータを使用して、Controllerを論理的なグループに分類します。これにより、Swagger UI上でエンドポイントが整理され、見やすくなります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  findAll() {
    return this.tasksService.findAll();
  }

  @Post()
  create(@Body() createTaskDto: CreateTaskDto) {
    return this.tasksService.create(createTaskDto);
  }
}

@ApiTags()は複数のタグを指定することも可能です。

1
2
3
@ApiTags('tasks', 'management')
@Controller('tasks')
export class TasksController {}

@ApiOperation()によるエンドポイント説明の追加

@ApiOperation()デコレータを使用して、各エンドポイントに対する詳細な説明を追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { Controller, Get, Post, Body, Param, Delete } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  @ApiOperation({
    summary: 'すべてのタスクを取得',
    description: '登録されているすべてのタスクの一覧を返却します。ページネーションには対応していません。',
  })
  findAll() {
    return this.tasksService.findAll();
  }

  @Get(':id')
  @ApiOperation({
    summary: '指定IDのタスクを取得',
    description: 'パスパラメータで指定されたIDのタスクを1件返却します。存在しない場合は404エラーを返します。',
  })
  findOne(@Param('id') id: string) {
    return this.tasksService.findOne(id);
  }

  @Post()
  @ApiOperation({
    summary: '新規タスクを作成',
    description: 'リクエストボディの内容で新規タスクを作成し、作成されたタスクを返却します。',
  })
  create(@Body() createTaskDto: CreateTaskDto) {
    return this.tasksService.create(createTaskDto);
  }

  @Delete(':id')
  @ApiOperation({
    summary: 'タスクを削除',
    description: '指定されたIDのタスクを削除します。',
  })
  remove(@Param('id') id: string) {
    return this.tasksService.remove(id);
  }
}

@ApiOperation()のオプションを以下に示します。

プロパティ 説明
summary 操作の簡潔な説明(Swagger UIの一覧に表示)
description 操作の詳細な説明
operationId 操作の一意な識別子(クライアント生成時に使用)
deprecated 操作が非推奨かどうか

@ApiResponse()によるレスポンスの定義

@ApiResponse()デコレータを使用して、各エンドポイントが返すレスポンスの詳細を定義します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { Controller, Get, Post, Body, Param, Delete, NotFoundException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';
import { Task } from './entities/task.entity';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  @ApiOperation({ summary: 'すべてのタスクを取得' })
  @ApiResponse({
    status: 200,
    description: 'タスク一覧の取得に成功',
    type: [Task],
  })
  findAll(): Task[] {
    return this.tasksService.findAll();
  }

  @Get(':id')
  @ApiOperation({ summary: '指定IDのタスクを取得' })
  @ApiResponse({
    status: 200,
    description: 'タスクの取得に成功',
    type: Task,
  })
  @ApiResponse({
    status: 404,
    description: '指定されたIDのタスクが存在しない',
  })
  findOne(@Param('id') id: string): Task {
    return this.tasksService.findOne(id);
  }

  @Post()
  @ApiOperation({ summary: '新規タスクを作成' })
  @ApiResponse({
    status: 201,
    description: 'タスクの作成に成功',
    type: Task,
  })
  @ApiResponse({
    status: 400,
    description: 'リクエストボディのバリデーションエラー',
  })
  create(@Body() createTaskDto: CreateTaskDto): Task {
    return this.tasksService.create(createTaskDto);
  }
}

NestJSには、よく使用されるHTTPステータスコードに対応したショートハンドデコレータも用意されています。

デコレータ 対応ステータス
@ApiOkResponse() 200 OK
@ApiCreatedResponse() 201 Created
@ApiBadRequestResponse() 400 Bad Request
@ApiUnauthorizedResponse() 401 Unauthorized
@ApiForbiddenResponse() 403 Forbidden
@ApiNotFoundResponse() 404 Not Found
@ApiInternalServerErrorResponse() 500 Internal Server Error

これらを使用すると、コードがより簡潔になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import {
  ApiTags,
  ApiOperation,
  ApiOkResponse,
  ApiCreatedResponse,
  ApiBadRequestResponse,
  ApiNotFoundResponse,
} from '@nestjs/swagger';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  @Get(':id')
  @ApiOperation({ summary: '指定IDのタスクを取得' })
  @ApiOkResponse({ description: 'タスクの取得に成功', type: Task })
  @ApiNotFoundResponse({ description: '指定されたIDのタスクが存在しない' })
  findOne(@Param('id') id: string): Task {
    return this.tasksService.findOne(id);
  }

  @Post()
  @ApiOperation({ summary: '新規タスクを作成' })
  @ApiCreatedResponse({ description: 'タスクの作成に成功', type: Task })
  @ApiBadRequestResponse({ description: 'バリデーションエラー' })
  create(@Body() createTaskDto: CreateTaskDto): Task {
    return this.tasksService.create(createTaskDto);
  }
}

@ApiProperty()によるDTOプロパティの定義

Swagger UIでリクエストボディやレスポンスのスキーマを正しく表示するには、DTOクラスのプロパティに@ApiProperty()デコレータを付与します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsString, IsNotEmpty, IsEnum, IsOptional, MaxLength } from 'class-validator';

export enum TaskStatus {
  TODO = 'TODO',
  IN_PROGRESS = 'IN_PROGRESS',
  DONE = 'DONE',
}

export class CreateTaskDto {
  @ApiProperty({
    description: 'タスクのタイトル',
    example: 'ドキュメントの作成',
    maxLength: 100,
  })
  @IsString()
  @IsNotEmpty()
  @MaxLength(100)
  title: string;

  @ApiPropertyOptional({
    description: 'タスクの詳細説明',
    example: 'OpenAPIの導入ガイドを作成する',
  })
  @IsString()
  @IsOptional()
  description?: string;

  @ApiProperty({
    description: 'タスクのステータス',
    enum: TaskStatus,
    example: TaskStatus.TODO,
  })
  @IsEnum(TaskStatus)
  status: TaskStatus;
}

@ApiProperty()の主要なオプションを以下に示します。

オプション 説明
description プロパティの説明
example 値の例
required 必須かどうか(デフォルトはtrue
type プロパティの型
enum 列挙型の値
minimum / maximum 数値の最小値/最大値
minLength / maxLength 文字列の最小長/最大長
default デフォルト値
nullable null許容かどうか

任意プロパティには@ApiPropertyOptional()を使用します。これは@ApiProperty({ required: false })と同等です。

Entityクラスへの@ApiProperty()適用

レスポンス型として使用するEntityクラスにも@ApiProperty()を適用することで、レスポンススキーマがSwagger UIに正しく表示されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { ApiProperty } from '@nestjs/swagger';

export class Task {
  @ApiProperty({
    description: 'タスクの一意識別子',
    example: 'uuid-1234-5678',
  })
  id: string;

  @ApiProperty({
    description: 'タスクのタイトル',
    example: 'ドキュメントの作成',
  })
  title: string;

  @ApiProperty({
    description: 'タスクの詳細説明',
    example: 'OpenAPIの導入ガイドを作成する',
    nullable: true,
  })
  description: string | null;

  @ApiProperty({
    description: 'タスクのステータス',
    enum: ['TODO', 'IN_PROGRESS', 'DONE'],
    example: 'TODO',
  })
  status: string;

  @ApiProperty({
    description: 'タスクの作成日時',
    example: '2026-01-06T18:00:00.000Z',
  })
  createdAt: Date;

  @ApiProperty({
    description: 'タスクの更新日時',
    example: '2026-01-06T18:30:00.000Z',
  })
  updatedAt: Date;
}

@ApiParam()と@ApiQuery()によるパラメータ定義

パスパラメータとクエリパラメータに詳細な説明を追加するには、@ApiParam()@ApiQuery()デコレータを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { Controller, Get, Query, Param } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiParam, ApiQuery, ApiOkResponse } from '@nestjs/swagger';
import { TasksService } from './tasks.service';
import { Task } from './entities/task.entity';
import { TaskStatus } from './dto/create-task.dto';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  @ApiOperation({ summary: 'タスク一覧を取得' })
  @ApiQuery({
    name: 'status',
    required: false,
    enum: TaskStatus,
    description: 'ステータスでフィルタリング',
  })
  @ApiQuery({
    name: 'limit',
    required: false,
    type: Number,
    description: '取得件数の上限',
    example: 10,
  })
  @ApiOkResponse({ description: 'タスク一覧の取得に成功', type: [Task] })
  findAll(
    @Query('status') status?: TaskStatus,
    @Query('limit') limit?: number,
  ): Task[] {
    return this.tasksService.findAll({ status, limit });
  }

  @Get(':id')
  @ApiOperation({ summary: '指定IDのタスクを取得' })
  @ApiParam({
    name: 'id',
    description: 'タスクの一意識別子',
    example: 'uuid-1234-5678',
  })
  @ApiOkResponse({ description: 'タスクの取得に成功', type: Task })
  findOne(@Param('id') id: string): Task {
    return this.tasksService.findOne(id);
  }
}

@ApiBody()によるリクエストボディの定義

@ApiBody()デコレータを使用すると、リクエストボディに関する追加情報を明示的に指定できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { Controller, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBody, ApiCreatedResponse } from '@nestjs/swagger';
import { CreateTaskDto } from './dto/create-task.dto';
import { Task } from './entities/task.entity';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
  @Post()
  @ApiOperation({ summary: '新規タスクを作成' })
  @ApiBody({
    type: CreateTaskDto,
    description: '作成するタスクの情報',
    examples: {
      example1: {
        summary: '基本的なタスク作成',
        value: {
          title: 'ミーティング準備',
          description: '資料の印刷と会議室の予約',
          status: 'TODO',
        },
      },
      example2: {
        summary: '説明なしのタスク作成',
        value: {
          title: '買い物',
          status: 'TODO',
        },
      },
    },
  })
  @ApiCreatedResponse({ description: 'タスクの作成に成功', type: Task })
  create(@Body() createTaskDto: CreateTaskDto): Task {
    return this.tasksService.create(createTaskDto);
  }
}

Bearer認証の設定

JWT認証などのBearer認証を使用するAPIでは、DocumentBuilderで認証スキームを追加し、各エンドポイントに@ApiBearerAuth()デコレータを適用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Task Management API')
    .setDescription('タスク管理アプリケーションのREST API仕様書')
    .setVersion('1.0')
    .addBearerAuth(
      {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
        description: 'JWTトークンを入力してください',
      },
      'access-token',
    )
    .build();

  const documentFactory = () => SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api-docs', app, documentFactory);

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

認証が必要なエンドポイントに@ApiBearerAuth()を適用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';

@ApiTags('tasks')
@ApiBearerAuth('access-token')
@UseGuards(JwtAuthGuard)
@Controller('tasks')
export class TasksController {
  @Get()
  @ApiUnauthorizedResponse({ description: '認証に失敗しました' })
  findAll() {
    return this.tasksService.findAll();
  }
}

Swagger UIの画面右上に「Authorize」ボタンが表示され、JWTトークンを入力してAPI呼び出しをテストできるようになります。

グローバルレスポンスの設定

すべてのエンドポイントに共通するレスポンス(認証エラーやサーバーエラーなど)は、DocumentBuilderaddGlobalResponse()メソッドで一括定義できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const config = new DocumentBuilder()
  .setTitle('Task Management API')
  .setDescription('タスク管理アプリケーションのREST API仕様書')
  .setVersion('1.0')
  .addBearerAuth()
  .addGlobalResponse({
    status: 401,
    description: '認証が必要です',
  })
  .addGlobalResponse({
    status: 500,
    description: 'サーバー内部エラー',
  })
  .build();

SwaggerModuleの詳細オプション

SwaggerModule.setup()の第4引数でSwagger UIの表示をカスタマイズできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
SwaggerModule.setup('api-docs', app, documentFactory, {
  customSiteTitle: 'Task API Documentation',
  customCss: '.swagger-ui .topbar { display: none }',
  swaggerOptions: {
    persistAuthorization: true,
    docExpansion: 'none',
    filter: true,
    showRequestDuration: true,
  },
});

主要なオプションを以下に示します。

オプション 説明
customSiteTitle ブラウザタブに表示されるタイトル
customCss カスタムCSSスタイル
swaggerOptions.persistAuthorization 認証情報を保持する
swaggerOptions.docExpansion ドキュメントの展開状態('list', 'full', 'none'
swaggerOptions.filter 検索フィルタを表示する

Swagger構成の全体像

以下のMermaid図は、NestJSアプリケーションでSwaggerを構成する要素の関係を示しています。

flowchart TB
    subgraph Main["main.ts"]
        DB[DocumentBuilder]
        SM[SwaggerModule]
    end

    subgraph Decorators["デコレータ"]
        AT["@ApiTags()"]
        AO["@ApiOperation()"]
        AR["@ApiResponse()"]
        AP["@ApiProperty()"]
        AQ["@ApiQuery()"]
        APM["@ApiParam()"]
        AB["@ApiBearerAuth()"]
    end

    subgraph Components["アプリケーション構成要素"]
        C[Controller]
        DTO[DTO]
        E[Entity]
    end

    subgraph Output["出力"]
        UI[Swagger UI]
        JSON[OpenAPI JSON]
        YAML[OpenAPI YAML]
    end

    DB --> SM
    SM --> UI
    SM --> JSON
    SM --> YAML

    AT --> C
    AO --> C
    AR --> C
    AQ --> C
    APM --> C
    AB --> C

    AP --> DTO
    AP --> E

    C --> SM
    DTO --> SM
    E --> SM

期待される結果

本記事の手順を完了すると、以下の状態になります。

  1. http://localhost:3000/api-docsでインタラクティブなSwagger UIにアクセスできる
  2. 各エンドポイントに対して、操作の説明、リクエストパラメータ、レスポンススキーマが表示される
  3. Swagger UIから直接APIリクエストを送信してテストできる
  4. /api-docs-jsonおよび/api-docs-yamlでOpenAPI仕様ファイルをダウンロードできる
  5. Bearer認証を使用するエンドポイントでは、トークンを入力して認証済みリクエストを送信できる

まとめ

本記事では、@nestjs/swaggerパッケージを使用してNestJSアプリケーションにOpenAPI仕様のAPI仕様書を自動生成する方法を解説しました。

主要なポイントは以下のとおりです。

  • SwaggerModule.setup()でSwagger UIを公開する
  • @ApiTags()でエンドポイントを論理的にグループ化する
  • @ApiOperation()で各操作の説明を追加する
  • @ApiResponse()およびショートハンドデコレータでレスポンスを定義する
  • @ApiProperty()でDTOとEntityのスキーマを定義する
  • @ApiParam()@ApiQuery()でパラメータに詳細情報を追加する
  • addBearerAuth()@ApiBearerAuth()で認証を設定する

コードと同期したAPI仕様書を自動生成することで、ドキュメントの保守コストを削減し、開発者間のコミュニケーションを効率化できます。

参考リンク