[NEST.JS] OPENAPI

birdgang
27 min readFeb 4, 2024

--

nestjs, openapi

NestJS 에서 OpenAPI(Swagger)는 API 의 명세를 정의 하고, 문서화 하는 데 사용 됩니다. OpenAPI 는 API 의 엔드포인트, 요청 및 응답 형식, 인증 메커니즘 등을 명세화 하여 API 를 쉽게 이해하고 사용할 수 있게 도와줍니다. NestJS 는 Swagger 모듈을 통해 이러한 OpenAPI 명세를 쉽게 구성하고 자동으로 문서를 생성 할 수 있도록 지원 합니다.

Features ?

  1. Swagger 모듈 설치 :
npm install --save @nestjs/swagger

2. Swagger 설정 및 초기화 : 애플리케이션의 main.ts 파일에서 Swagger 모듈을 설정 하고 초기화 합니다.

3. 컨트롤러 와 모델에 Swagger 데코레이터 추가 : API 엔드포인트 및 DTO(Data Transfer Objects)에 Swagger 데코레이터 를 추가 하여 API 명세를 정의 합니다.

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

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

const config = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);

await app.listen(3000);
}
bootstrap();

main.ts 파일에서 SwaggerModule을 사용 하여 Swagger 문서를 생성 하고 설정 합니다.

// cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { CreateCatDto } from './create-cat.dto';

@Controller('cats')
@ApiTags('cats')
export class CatsController {
@Post()
@ApiOperation({ summary: 'Create cat' })
async create(@Body() createCatDto: CreateCatDto) {
// ...
}

@Get()
@ApiOperation({ summary: 'Get all cats' })
async findAll() {
// ...
}
}

CatsController에서 ApiOperationApiTags 데코레이터를 사용 하여 각 엔드포인트의 목적과 분류를 명시 합니다.

// create-cat.dto.ts
import { ApiProperty } from '@nestjs/swagger';

export class CreateCatDto {
@ApiProperty()
name: string;

@ApiProperty()
age: number;

@ApiProperty()
breed: string;
}

CreateCatDto 클래스에서 ApiProperty 데코레이터를 사용하여 각 필드의 명세를 정의 합니다.

Swagger 를 통해 생성된 API 문서는 /api 경로 에서 확인 할 수 있으며, 이 문서를 통해 개발자들은 API 의 사용 방법을 쉽게 파악하고 테스트 할 수 있습니다. NestJS 와 Swagger 를 사용 하면, API 개발 과정에서의 효율성을 높이고, API 사용자에게 명확한 가이드라인을 제공 할 수 있습니다.

Types and parameters ?

API 의 타입과 파라미터를 명세화 하는 것은 API 의 구조와 요구 사항을 명확하게 문서화 하는 데 중요 합니다. 이를 통해 API 의 요청 및 응답 형식, 파라미터 타입, 필요한 헤더 등을 정의 할 수 있으며, API 사용자에게 이러한 정보를 명확하게 제공 할 수 있습니다.

Features ?

  1. DTO(Data Transfer Object) 클래스에 타입 정의 : DTO 클래스를 사용 하여 요청 및 응답의 데이터 구조를 정의 합니다. @ApiProperty 데코레이터를 사용 하여 각 필드의 타입과 설명을 명시 합니다.
  2. 컨트롤러에서 파라미터 정의 : API 엔드포인트 에서 사용 되는 파라미터의 타입과 필요성을 명시 합니다. @ApiParam, @ApiQuery, @ApiBody 등의 데코레이터 를 사용 합니다.
// create-cat.dto.ts
import { ApiProperty } from '@nestjs/swagger';

export class CreateCatDto {
@ApiProperty({ example: 'Kitty', description: 'The name of the cat' })
name: string;

@ApiProperty({ example: 3, description: 'The age of the cat' })
age: number;

@ApiProperty({ example: 'Maine Coon', description: 'The breed of the cat' })
breed: string;
}

CreateCatDto 클래스에서 @ApiProperty를 사용 하여 각 필드의 예시 및 설명을 제공 합니다.

// cats.controller.ts
import { Controller, Get, Post, Body, Param, Query } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBody, ApiParam, ApiQuery } from '@nestjs/swagger';
import { CreateCatDto } from './create-cat.dto';

@Controller('cats')
@ApiTags('cats')
export class CatsController {
@Post()
@ApiOperation({ summary: 'Create cat' })
@ApiBody({ type: CreateCatDto })
async create(@Body() createCatDto: CreateCatDto) {
// ...
}

@Get(':id')
@ApiOperation({ summary: 'Get a cat by id' })
@ApiParam({ name: 'id', type: String, description: 'Cat ID' })
async findOne(@Param('id') id: string) {
// ...
}

@Get()
@ApiOperation({ summary: 'Get cats by breed' })
@ApiQuery({ name: 'breed', type: String, required: false, description: 'Cat breed' })
async findByBreed(@Query('breed') breed?: string) {
// ...
}
}

CatsController에서 @ApiBody, @ApiParam, @ApiQuery를 사용 하여 API 엔드포인트의 파라미터 타입과 설명을 정의 합니다.

Operations ?

API 의 Operations(작업)을 정의 하는 것은, 각 API 엔드포인트 의 목적, 요청 및 응답의 세부 사항을 명세화 하는 데 중요 합니다. 이를 통해 개발자들이 API 를 더 쉽게 이해하고 사용 할 수 있도록 돕습니다.

Features ?

  1. 컨트롤러에서 API 작업 정의 : @ApiOperation 데코레이터 를 사용하여 각 API 엔드포인트 의 작업에 대한 설명을 추가 합니다. 요청의 종류(GET, POST, PUT, DELETE 등)에 따라 적절한 데코레이터 를 사용합니다.
  2. 요청 및 응답 형식 정의 : @ApiBody, @ApiResponse 데코레이터 를 사용 하여 요청 본문과 응답 형식을 정의 합니다.
// cats.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ApiOperation, ApiTags, ApiBody, ApiResponse } from '@nestjs/swagger';
import { CreateCatDto } from './create-cat.dto';
import { Cat } from './cat.entity';

@Controller('cats')
@ApiTags('cats')
export class CatsController {
@Post()
@ApiOperation({ summary: 'Create a new cat' })
@ApiBody({ type: CreateCatDto })
@ApiResponse({ status: 201, description: 'The cat has been created.' })
async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
// ...
}

@Get(':id')
@ApiOperation({ summary: 'Get a cat by ID' })
@ApiResponse({ status: 200, type: Cat })
async findOne(@Param('id') id: string): Promise<Cat> {
// ...
}
}

CatsController에서 @ApiOperation, @ApiBody, @ApiResponse를 사용 하여 각 API 엔드포인트 의 작업을 정의 합니다.

// create-cat.dto.ts
import { ApiProperty } from '@nestjs/swagger';

export class CreateCatDto {
@ApiProperty()
readonly name: string;

@ApiProperty()
readonly age: number;

@ApiProperty()
readonly breed: string;
}

// cat.entity.ts
export class Cat {
id: number;
name: string;
age: number;
breed: string;
}

CreateCatDtoCat 클래스에서 @ApiProperty를 사용 하여 요청과 응답의 데이터 구조를 정의 합니다.

Security ?

API 의 보안 설정을 정의 하는 것은, API 의 인증 및 권한 부여 메커니즘을 문서화 하는 데 중요 합니다. 이를 통해 API 사용자들이 필요한 보안 요건을 이해하고, 적절한 인증을 통해 API 를 사용할 수 있도록 안내 합니다.

Features ?

  1. 보안 스키마 정의 : DocumentBuilder를 사용하여 API 의 보안 스키마를 정의 합니다. 예를 들어, JWT 기반 인증을 사용할 경우, bearerFormatscheme를 정의 할 수 있습니다.
  2. 컨트롤러에서 보안 스키마 적용 : @ApiSecurity 데코레이터 를 사용 하여 특정 API 엔드포인트 에 보안 요구사항을 적용 합니다.
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

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

const config = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addBearerAuth(
{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
'access-token',
)
.build();

const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);

await app.listen(3000);
}
bootstrap();

main.ts 파일에서 addBearerAuth 메서드 를 사용 하여 JWT 기반의 인증 스키마를 정의 합니다.

// cats.controller.ts
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiBody } from '@nestjs/swagger';
import { JwtAuthGuard } from './jwt-auth.guard';
import { CreateCatDto } from './create-cat.dto';

@Controller('cats')
@ApiTags('cats')
@ApiBearerAuth('access-token')
export class CatsController {
@Post()
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Create a new cat' })
@ApiBody({ type: CreateCatDto })
async create(@Body() createCatDto: CreateCatDto) {
// ...
}

// 다른 메서드들...
}

CatsController에서 @ApiBearerAuth 데코레이터 를 사용 하여 JWT 인증을 요구하는 엔드포인트 를 정의 합니다.

API 문서에는 JWT 토큰을 사용한 인증 방법이 명시 되어 있으며, CatsControllercreate 메서드는 JWT 인증을 통과 해야 접근 가능 합니다. 이렇게 NestJS 와 OpenAPI 를 사용 하여 API 보안 요구사항을 명확 하게 문서화하면, API 사용자는 필요한 인증 절차를 쉽게 이해하고 준수할 수 있습니다.

Mapped types ?

Mapped Types 는 기존 클래스를 기반으로 새로운 형태의 클래스를 생성 하는 기능 입니다. 이를 통해 DRY(Don’t Repeat Yourself) 원칙을 따르며, 코드의 재사용성 을 높일 수 있습니다. 예를 들어, 기본 DTO 클래스에서 일부 필드를 선택적으로 만들거나, 특정 필드만 포함하는 새로운 DTO 클래스를 생성 할 수 있습니다.

Features ?

  1. 기본 DTO 클래스 정의 : 기본 데이터 전송 객체(Data Transfer Object, DTO) 클래스를 정의 합니다.
  2. Mapped Types 생성 : PartialType, PickType, OmitType 등의 NestJS 유틸리티 함수를 사용하여 새로운 형태의 DTO 클래스를 생성 합니다.
// create-cat.dto.ts
import { ApiProperty } from '@nestjs/swagger';

export class CreateCatDto {
@ApiProperty()
readonly name: string;

@ApiProperty()
readonly age: number;

@ApiProperty()
readonly breed: string;
}

CreateCatDto 클래스에서 고양이를 생성 하기 위한 기본 필드를 정의 합니다.

// update-cat.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateCatDto } from './create-cat.dto';

export class UpdateCatDto extends PartialType(CreateCatDto) {}

UpdateCatDto 클래스는 CreateCatDto를 기반으로 하되, 모든 필드가 선택적(Optional)입니다.

// cats.controller.ts
import { Controller, Get, Put, Body, Param } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { UpdateCatDto } from './update-cat.dto';

@Controller('cats')
@ApiTags('cats')
export class CatsController {
@Put(':id')
@ApiOperation({ summary: 'Update a cat' })
async update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
// ...
}

// 다른 메서드들...
}

CatsController에서 UpdateCatDto를 사용하여 고양이 정보를 업데이트 하는 API 엔드포인트 를 정의 합니다.

Decorators ?

NestJS 에서 OpenAPI 를 위한 데코레이터들 은 API 문서를 자동으로 생성하고, 엔드포인트 의 메타데이터 를 상세하게 설명하는 데 사용 됩니다. 이 데코레이터들 을 활용 하면, API 의 파라미터, 응답 타입, HTTP 상태 코드, 보안 요구 사항 등을 명세할 수 있으며, Swagger 와 같은 도구를 통해 이러한 정보를 기반으로 한 문서를 제공할 수 있습니다.

Features ?

  • @ApiTags(...): 컨트롤러 레벨에서 API 태그를 지정 합니다.
  • @ApiOperation({ summary: '...' }): 각 API 연산에 대한 요약을 제공 합니다.
  • @ApiParam({ name: '...', description: '...' }): URL 파라미터에 대한 정보를 제공 합니다.
  • @ApiBody({ type: ... }): 요청 본문의 타입과 설명을 명시 합니다.
  • @ApiResponse({ status: ..., description: '...', type: ... }): 응답에 대한 상태 코드, 설명, 반환 타입을 정의 합니다.
  • @ApiProperty(...): DTO 클래스의 각 필드에 대한 설명을 제공 합니다.
  • @ApiBearerAuth(): 인증이 필요한 API에 대해 Bearer 토큰 인증을 사용함을 명시 합니다.
// cats.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBody, ApiResponse, ApiParam } from '@nestjs/swagger';
import { CreateCatDto, Cat } from './cats.dto';

@ApiTags('cats')
@Controller('cats')
export class CatsController {
@Post()
@ApiOperation({ summary: 'Create a new cat' })
@ApiBody({ type: CreateCatDto })
@ApiResponse({ status: 201, description: 'The cat has been successfully created.', type: Cat })
async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
// 로직 구현
}

@Get(':id')
@ApiOperation({ summary: 'Get a cat by ID' })
@ApiParam({ name: 'id', description: 'The cat ID' })
@ApiResponse({ status: 200, description: 'The cat details', type: Cat })
async findOne(@Param('id') id: string): Promise<Cat> {
// 로직 구현
}
}
// cats.dto.ts
import { ApiProperty } from '@nestjs/swagger';

export class CreateCatDto {
@ApiProperty({ example: 'Kitty', description: 'Name of the cat' })
name: string;

@ApiProperty({ example: 3, description: 'Age of the cat' })
age: number;

@ApiProperty({ example: 'Maine Coon', description: 'Breed of the cat' })
breed: string;
}

export class Cat extends CreateCatDto {
@ApiProperty({ example: 1, description: 'The ID of the cat' })
id: number;
}

이 사용 사례에서는 @ApiTags, @ApiOperation, @ApiBody, @ApiResponse, @ApiParam, @ApiProperty 등의 데코레이터를 사용 하여 API 엔드포인트와 관련 DTO 클래스를 정의 하고 있습니다. 이를 통해 생성된 OpenAPI 문서는 개발자가 API 를 더 쉽게 이해하고 사용할 수 있도록 상세한 정보를 제공 합니다.

CLI Plugin ?

CLI 를 활용 하여 OpenAPI 스펙을 기반으로 한 타입스크립트 인터페이스와 클래스를 자동으로 생성 해주는 도구 입니다. 이 플러그인을 사용하면, OpenAPI 명세에서 정의된 모델을 바탕으로 바로 NestJS 서비스에서 사용 할 수 있는 타입을 생성 할 수 있어, 개발 과정을 효율적으로 만들어 줍니다.

npm install --save-dev @nestjs/swagger @nestjs/cli-plugin-openapi
  1. OpenAPI 스펙 정의 파일 준비 : 프로젝트에 cats.openapi.yaml 같은 OpenAPI 스펙 정의 파일을 준비 합니다.
  2. CLI 명령어 실행 : NestJS CLI 를 사용하여 OpenAPI 스펙 을 바탕으로 DTO 클래스를 생성합니다. 터미널에서 다음과 같은 명령어를 실행합니다.
nest generate openapi-class src/cats.openapi.yaml

이 명령어는 src/cats.openapi.yaml 파일에 정의된 OpenAPI 스펙 을 분석 하여, 해당 스펙 에 맞는 DTO 클래스를 프로젝트 내에 생성 합니다.

openapi: 3.0.0
info:
title: Cats Service
version: 1.0.0
paths:
/cats:
get:
summary: Lists all cats
responses:
'200':
description: A JSON array of cats
content:
application/json:
schema:
$ref: '#/components/schemas/CatArray'
components:
schemas:
CatArray:
type: array
items:
$ref: '#/components/schemas/Cat'
Cat:
type: object
properties:
id:
type: integer
name:
type: string
age:
type: integer
breed:
type: string

실행한 CLI 명령어는 CatCatArray 스키마를 바탕으로 해당하는 타입스크립트 클래스 파일들을 프로젝트 내에 생성 합니다. 생성된 클래스 는 NestJS 서비스에서 바로 사용할 수 있으며, API 의 요청과 응답에 대한 타입 안정성 을 제공합니다.

Other features ?

NestJS 의 OpenAPI 모듈은 API 문서화를 위한 강력한 기능과 함께 다양한 추가 기능들을 제공 합니다. 이러한 기능들은 API 설계와 문서화 과정을 더욱 효율적이고 강력하게 만들어줍니다. 여기서는 몇 가지 주요 추가 기능과 이를 활용한 실제 사용 사례를 소개 합니다.

Features ?

  1. API 문서 커스터마이징 : 문서의 타이틀, 설명, 버전 등을 커스터 마이징 할 수 있습니다.
  2. 글로벌 및 로컬 스키마 데코레이터 : 전역적 혹은 특정 핸들러 에 대한 스키마를 정의하여 중복을 줄일 수 있습니다.
  3. 파일 업로드 및 다운로드 : Multipart 요청을 처리하고 파일 업로드 API 를 문서화 할 수 있습니다.
  4. 보안 스키마 : 다양한 인증 방법(Bearer, OAuth2 등)을 문서에 명시할 수 있습니다.
  5. 고급 데이터 모델링 : Generic, Inheritance, Polymorphism 등을 활용한 고급 데이터 모델링이 가능 합니다.
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

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

const options = new DocumentBuilder()
.setTitle('Cats Service')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.addBearerAuth()
.build();

const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);

await app.listen(3000);
}
bootstrap();
// cats.controller.ts
import { Controller, Post, UseInterceptors, UploadedFile, Body } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiTags, ApiOperation, ApiConsumes, ApiBody } from '@nestjs/swagger';

@ApiTags('cats')
@Controller('cats')
export class CatsController {

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
@ApiOperation({ summary: 'Upload cat image' })
@ApiConsumes('multipart/form-data')
@ApiBody({
schema: {
type: 'object',
properties: {
file: {
type: 'string',
format: 'binary',
},
},
},
})
uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log(file);
}
}

DocumentBuilder를 사용 하여 API 문서의 기본 설정 을 커스터마이징했으며, 파일 업로드 API 를 문서화 하는 방법을 보여 줍니다. 파일 업로드를 위해 @ApiConsumes@ApiBody 데코레이터 를 사용 하여 multipart/form-data 요청과 요청 본문의 스키마를 정의 했습니다.

--

--