NestJS course Β· Module 11: Swagger and OpenAPI
DTO Documentation - Seals on Parchment
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:
| Option | Description | Example |
|---|---|---|
description | Field description | 'Name of the legionary' |
example | Example value | 'Marcus' |
required | Whether the field is required | true / false |
default | Default value | 'Miles' |
enum | List of allowed values | ['Centurio', 'Miles'] |
type | Field type | String, Number, Boolean |
isArray | Whether the field is an array | true |
minimum | Minimum value (number) | 0 |
maximum | Maximum value (number) | 100 |
minLength | Minimum length (string) | 2 |
maxLength | Maximum 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");
51Spotted 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. What does the @ApiProperty() decorator do on a DTO class field?
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