NestJS course Β· Module 11: Swagger and OpenAPI

DTO Documentation - Seals on Parchment

3 min read
In this lesson7

Every document in the imperial chancellery had to have precisely described fields - the petitioner's name, the type of matter, the date, and the seal. In NestJS, the equivalent of these documents are DTOs (Data Transfer Objects), and the @ApiProperty() decorator describes each field so that Swagger UI can display it.

@ApiProperty - Basics

The @ApiProperty() decorator describes a single field in a DTO class:

1import { ApiProperty } from '@nestjs/swagger';
2
3export class CreateLegionaryDto {
4  @ApiProperty({
5    description: 'Name of the legionary',
6    example: 'Marcus Aurelius',
7  })
8  name: string;
9
10  @ApiProperty({
11    description: 'Rank in the legion',
12    example: 'Centurio',
13  })
14  rank: string;
15
16  @ApiProperty({
17    description: 'Years of experience',
18    example: 5,
19    minimum: 0,
20    maximum: 40,
21  })
22  experience: number;
23}

@ApiProperty Options

The decorator accepts many configuration options:

OptionDescriptionExample
descriptionField description'Name of the legionary'
exampleExample value'Marcus'
requiredWhether the field is requiredtrue / false
defaultDefault value'Miles'
enumList of allowed values['Centurio', 'Miles']
typeField typeString, Number, Boolean
isArrayWhether the field is an arraytrue
minimumMinimum value (number)0
maximumMaximum value (number)100
minLengthMinimum length (string)2
maxLengthMaximum length (string)50

Optional Fields with @ApiPropertyOptional

For optional fields, we use @ApiPropertyOptional():

1import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
3export class UpdateLegionaryDto {
4  @ApiPropertyOptional({
5    description: 'New rank of the legionary',
6    example: 'Optio',
7  })
8  rank?: string;
9
10  @ApiPropertyOptional({
11    description: 'New assigned legion',
12    example: 'Legio III Augusta',
13  })
14  legio?: string;
15
16  @ApiPropertyOptional({
17    description: 'Additional experience',
18    example: 2,
19    minimum: 0,
20  })
21  additionalExperience?: number;
22}

Fields with enum - Legion Ranks

Enum is particularly useful for fields with a limited set of values:

1export enum LegionaryRank {
2  MILES = 'Miles',
3  OPTIO = 'Optio',
4  CENTURIO = 'Centurio',
5  TRIBUNUS = 'Tribunus',
6  LEGATUS = 'Legatus',
7}
8
9export class CreateLegionaryDto {
10  @ApiProperty({
11    description: 'Rank of the legionary',
12    enum: LegionaryRank,
13    example: LegionaryRank.MILES,
14  })
15  rank: LegionaryRank;
16}

Nested Objects - Legion Structure

For nested DTOs, we use the type option:

1export class AddressDto {
2  @ApiProperty({ description: 'Province name', example: 'Pannonia' })
3  province: string;
4
5  @ApiProperty({ description: 'City name', example: 'Aquincum' })
6  city: string;
7}
8
9export class LegionDto {
10  @ApiProperty({ description: 'Legion name', example: 'Legio X Gemina' })
11  name: string;
12
13  @ApiProperty({
14    description: 'Legion location',
15    type: AddressDto,
16  })
17  location: AddressDto;
18
19  @ApiProperty({
20    description: 'List of centurions',
21    type: [String],
22    example: ['Marcus', 'Julius', 'Gaius'],
23  })
24  centurions: string[];
25}

Response DTOs

It is a good practice to create separate DTOs for API responses:

1export class LegionaryResponseDto {
2  @ApiProperty({ description: 'Legionary ID', example: '507f1f77bcf86cd799439011' })
3  id: string;
4
5  @ApiProperty({ description: 'Name of the legionary', example: 'Marcus Aurelius' })
6  name: string;
7
8  @ApiProperty({ description: 'Rank', enum: LegionaryRank })
9  rank: LegionaryRank;
10
11  @ApiProperty({ description: 'Recruitment date', example: '2024-01-15T10:30:00Z' })
12  recruitedAt: Date;
13
14  @ApiProperty({ description: 'Is active', example: true })
15  isActive: boolean;
16}

Practical Exercise

Create DTO documentation for the Empire's province management system:

Code for this lesson: src/province.dto.ts
1// DTO documentation - seals on parchment
2import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
3
4// TODO: Create enum ProvinceStatus with values:
5// ACTIVE = 'Active', CONQUERED = 'Conquered', REBELLING = 'Rebelling'
6export enum ProvinceStatus {
7  // TODO: Complete the enum values
8}
9
10export class CreateProvinceDto {
11  // TODO: Add @ApiProperty with description and example
12  name: string;
13
14  // TODO: Add @ApiProperty with description and example
15  governor: string;
16
17  // TODO: Add @ApiProperty with enum ProvinceStatus
18  status: ProvinceStatus;
19
20  // TODO: Add @ApiPropertyOptional with description, example, minimum
21  population?: number;
22
23  // TODO: Add @ApiProperty with description and type [String]
24  legions: string[];
25}
26
27export class ProvinceResponseDto {
28  @ApiProperty({ description: 'Province ID', example: '64a1b2c3d4e5f6a7b8c9d0e1' })
29  id: string;
30
31  @ApiProperty({ description: 'Province name', example: 'Pannonia' })
32  name: string;
33
34  @ApiProperty({ description: 'Governor', example: 'Lucius Verus' })
35  governor: string;
36
37  @ApiProperty({ description: 'Province status', enum: ProvinceStatus })
38  status: ProvinceStatus;
39
40  @ApiPropertyOptional({ description: 'Population', example: 500000 })
41  population?: number;
42
43  @ApiProperty({ description: 'Annexation date' })
44  annexedAt: Date;
45}
46
47console.log("@ApiProperty - required fields");
48console.log("@ApiPropertyOptional - optional fields");
49console.log("Options: description, example, enum, type");
50console.log("minimum, maximum, minLength, maxLength");
51

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What does the @ApiProperty() decorator do on a DTO class field?

  2. 2. Which decorator is used to mark optional fields in a DTO for Swagger?

These are 2 of 3 questions for this lesson. Solve the rest in the game.

Hands-on tasks in the game

  • Code editor

    Complete CreateCenturioDto by adding @ApiProperty with description and example to each field

  • Vertical ordering

    Arrange the elements of @ApiProperty with enum in the correct order:

  • Click in order

    Arrange the @ApiProperty options from description to value validation:

  • Code editor

    Complete CreateProvinceDto with enum ProvinceType, nested LocationDto, and a string array

Useful articles