NestJS OpenAPI Types and parameters

GGAE99·2023년 10월 25일
0

NestJS 공식 문서

목록 보기
31/33
post-thumbnail

Types and parameters

SwaggerModule는 라우트 핸들러에서 @Body(), @Query(), @Param() 데코레이터를 검색하여 API 문서를 생성한다. 또한 리플렉션을 활용하여 해당 모델 정의를 생성한다.

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

명시적으로 body 정의를 설정하려면 @ApiBody() 데코레이터를 사용

CreateCatDto를 기반으로 Swagger UI에서 다음 모델 정의가 생성된다.

모델 정의가 빈 것처럼 보이지만 클래스에는 몇 가지 속성이 선언되어 있다.
SwaggerModule에 클래스 속성을 표시하려면 속성에 @ApiProperty() 데코레이터를 지정하거나 이를 자동으로 수행하는 CLI 플러그인을 사용해야한다.

import { ApiProperty } from '@nestjs/swagger';

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

  @ApiProperty()
  age: number;

  @ApiProperty()
  breed: string;
}

Hint!
각 속성을 수동으로 주석 처리하는 대신, 이 작업을 자동으로 수행하는 Swagger 플러그인을 사용하는 것을 고려하자.

브라우저를 열고 생성된 CreateCatDto 모델을 확인하면 변화가 있다.

또한 @ApiProperty() 데코레이터를 사용하면 다양한 Schema Object 속성을 설정할 수 있다.

@ApiProperty({
  description: 'The age of a cat',
  minimum: 1,
  default: 1,
})
age: number;

Hint!
명시적으로 @ApiProperty({ required: false })를 입력하는 대신 @ApiPropertyOptional() 단축 데코레이터를 사용할 수 있다.

속성의 유형을 명시적으로 설정하려면 type 키를 사용하자.

@ApiProperty({
  type: Number,
})
age: number;

Arrays

속성이 배열인 경우 아래와 같이 배열 유형을 수동으로 표시해야한다.

@ApiProperty({ type: [String] })
names: string[];

Hint!
배열을 자동으로 감지할 수 있는 Swagger 플러그인을 고려하자!

배열인 경우 배열 유형을 배열의 첫 번째 요소로 포함하거나 isArray 속성을 true로 설정하자.

Circular dependencies

클래스 간에 환형 종속성이 있는 경우, SwaggerModule에 유형 정보를 제공하기 위해 지연 함수(lazy function)를 사용하자.

@ApiProperty({ type: () => Node })
node: Node;

위의 코드 예시에서 사용한 @ApiProperty({ type: () => Node })는 이러한 환형 종속성 상황에서 유용하게 사용되는 패턴이다. Node 클래스를 참조할 때 직접 유형을 명시하지 않고, 대신 함수를 사용하여 유형 정보를 제공한다. 함수가 호출될 때 Node 클래스를 반환하므로 SwaggerModule는 이 유형 정보를 사용하여 node 프로퍼티의 유형을 알 수 있다.

Generics and interfaces

TypeScript는 제네릭 또는 인터페이스에 대한 메타데이터를 저장하지 않으므로, DTO에서 제네릭 또는 인터페이스를 사용할 때 SwaggerModule은 런타임에서 모델 정의를 올바르게 생성하지 못할 수 있다.

createBulk(@Body() usersDto: CreateUserDto[])

이 제한을 극복하려면 유형을 명시적으로 설정해야한다.

@ApiBody({ type: [CreateUserDto] })
createBulk(@Body() usersDto: CreateUserDto[])

Enums

열거형(enum)을 식별하려면 @ApiPropertyenum 속성을 수동으로 설정해야 한다. 값 배열과 함께 enum 속성을 사용한다.

@ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
role: UserRole;

대신 다음과 같이 실제 TypeScript enum을 정의해서 사용할 수 있다.

export enum UserRole {
  Admin = 'Admin',
  Moderator = 'Moderator',
  User = 'User',
}

그런 다음 @Query() 매개 변수 데코레이터와 함께 @ApiQuery() 데코레이터를 사용하여 enum을 직접 사용할 수 있다.

@ApiQuery({ name: 'role', enum: UserRole })
async filterByRole(@Query('role') role: UserRole = UserRole.User) {}


isArraytrue로 설정하면 enum을 다중 선택으로 선택할 수 있다.

Enums schema

enum 속성은 매개 변수에 Enum의 parameter를 추가한다.

- breed:
    type: 'string'
    enum:
      - Persian
      - Tabby
      - Siamese

위 명세는 대부분의 경우에 잘 작동한다. 그러나 명세서를 입력으로 사용하고 클라이언트 측 코드를 생성하는 도구를 활용하는 경우, 생성된 코드에 중복된 열거형이 포함되는 문제가 발생할 수 있다.

// generated client-side code
export class CatDetail {
  breed: CatDetailEnum;
}

export class CatInformation {
  breed: CatInformationEnum;
}

export enum CatDetailEnum {
  Persian = 'Persian',
  Tabby = 'Tabby',
  Siamese = 'Siamese',
}

export enum CatInformationEnum {
  Persian = 'Persian',
  Tabby = 'Tabby',
  Siamese = 'Siamese',
}

Hint!
위의 코드 스니펫은 NSwag라는 도구를 사용하여 생성된다.

여기에서 동일한 열거형이 두 번 생성되었음을 확인할 수 있다.
이 문제를 해결하려면 데코레이터에서 enum 속성과 함께 enumName을 전달할 수 있다.

export class CatDetail {
  @ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
  breed: CatBreed;
}

enumName 속성을 사용하면 @nestjs/swaggerCatBreed를 자체 스키마로 변환하여 CatBreed 열거형을 재사용할 수 있게 된다.

CatDetail:
  type: 'object'
  properties:
    ...
    - breed:
        schema:
          $ref: '#/components/schemas/CatBreed'
CatBreed:
  type: string
  enum:
    - Persian
    - Tabby
    - Siamese

Hint!
enum을 속성으로 사용하는 데코레이터는 모두 enumName을 사용할 수 있다.

Raw definitions

특정 시나리오(예: deeply nested arrays, matrices)에서 유형을 수동으로 설명하고 싶을 수 있다.

@ApiProperty({
  type: 'array',
  items: {
    type: 'array',
    items: {
      type: 'number',
    },
  },
})
coords: number[][];

비슷하게, 컨트롤러 클래스에서 입력 및 출력 콘텐츠를 수동으로 정의하려면 schema 속성을 사용하자.

@ApiBody({
  schema: {
    type: 'array',
    items: {
      type: 'array',
      items: {
        type: 'number',
      },
    },
  },
})
async create(@Body() coords: number[][]) {}

Extra models

컨트롤러에서 직접 참조되지 않지만 Swagger 모듈에서 검사해야 하는 추가 모델을 정의하려면 @ApiExtraModels() 데코레이터를 사용하면된다.

@ApiExtraModels(ExtraModel)
export class CreateCatDto {}

Hint!
특정 모델 클래스에 대해 @ApiExtraModels()를 한 번만 사용하면 된다.

또는 SwaggerModule#createDocument() 메서드에 extraModels 속성이 지정된 옵션 객체를 전달할 수 있다.

const document = SwaggerModule.createDocument(app, options, {
  extraModels: [ExtraModel],
});

모델을 참조하기 위해 getSchemaPath(ExtraModel) 함수를 사용할 수 있다.

'application/vnd.api+json': {
   schema: { $ref: getSchemaPath(ExtraModel) },
},

oneOf, anyOf, allOf

스키마를 결합하기 위해 oneOf, anyOf 또는 allOf 키워드를 사용할 수 있다.
이러한 키워드는 스키마를 복합하거나 복수의 스키마를 결합할 때 사용된다.

// oneOf
@ApiProperty({
  oneOf: [
    { $ref: getSchemaPath(Cat) },
    { $ref: getSchemaPath(Dog) },
  ],
})
pet: Cat | Dog;

위의 예시에서 pet은 Cat 스키마 또는 Dog 스키마 중 하나일 수 있음을 나타낸다.

type Pet = Cat | Dog;

@ApiProperty({
  type: 'array',
  items: {
    oneOf: [
      { $ref: getSchemaPath(Cat) },
      { $ref: getSchemaPath(Dog) },
    ],
  },
})
pets: Pet[];

이 예시에서 pets는 Cat 또는 Dog와 같은 스키마를 가진 다중 스키마의 배열로 정의된다.
oneOf 키워드를 사용하여 배열의 각 원소는 여러 스키마 중 하나일 수 있음을 나타낸다.
getSchemaPath() 함수는 @nestjs/swagger에서 가져올 수 있는 함수로, 해당 스키마에 대한 $ref를 생성하는 데 사용된다.이를 통해 스키마를 참조하고 재사용할 수 있다.

0개의 댓글