Skip to content

Commit b7f67c0

Browse files
feat(): add mapped types section to swagger docs
1 parent fa6bfba commit b7f67c0

File tree

1 file changed

+146
-59
lines changed

1 file changed

+146
-59
lines changed

content/recipes/swagger.md

Lines changed: 146 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,138 @@ In order to explicitly set the type of the property, use the `type` key:
126126
age: number;
127127
```
128128

129+
#### Arrays
130+
131+
When the property is an array, we must manually indicate the array type as shown below:
132+
133+
```typescript
134+
@ApiProperty({ type: [String] })
135+
names: string[];
136+
```
137+
138+
> info **Hint** Consider using the Swagger plugin (see [Plugin](/recipes/swagger#plugin) section) which will automatically detect arrays.
139+
140+
Either include the type as the first element of an array (as shown above) or set the `isArray` property to `true`.
141+
142+
<app-banner-enterprise></app-banner-enterprise>
143+
144+
#### Circular dependencies
145+
146+
When you have circular dependencies between classes, use a lazy function to provide the `SwaggerModule` with type information:
147+
148+
```typescript
149+
@ApiProperty({ type: () => Node })
150+
node: Node;
151+
```
152+
153+
> info **Hint** Consider using the Swagger plugin (see [Plugin](/recipes/swagger#plugin) section) which will automatically detect circular dependencies.
154+
155+
#### Generics and interfaces
156+
157+
Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, `SwaggerModule` may not be able to properly generate model definitions at runtime. For instance, below code won't be correctly inspected by the Swagger module:
158+
159+
```typescript
160+
createBulk(@Body() usersDto: CreateUserDto[])
161+
```
162+
163+
In order to overcome this limitation, you can set the type explicitly:
164+
165+
```typescript
166+
@ApiBody({ type: [CreateUserDto] })
167+
createBulk(@Body() usersDto: CreateUserDto[])
168+
```
169+
170+
#### Mapped types
171+
172+
As you build out features like **CRUD** (Create/Read/Update/Delete) it's often useful to construct variants on a base entity type. Nest provides several utility functions that perform type transformations to make this task more convenient.
173+
174+
When building input validation types (also called DTOs), it's often useful to build **create** and **update** variations on the same type. For example, the **create** variant may require all fields, while the **update** variant may make all fields optional.
175+
176+
Nest provides the `PartialType()` utility function to make this task easier and minimize boilerplate.
177+
178+
The `PartialType()` function returns a type (class) with all the properties of the input type set to optional. For example, suppose we have a **create** type as follows:
179+
180+
```typescript
181+
import { ApiProperty } from '@nestjs/swagger';
182+
183+
export class CreateCatDto {
184+
@ApiProperty()
185+
name: string;
186+
187+
@ApiProperty()
188+
age: number;
189+
190+
@ApiProperty()
191+
breed: string;
192+
}
193+
```
194+
195+
y default, all of these fields are required. To create a type with the same fields, but with each one optional, use `PartialType()` passing the class reference (`CreateCatDto`) as an argument:
196+
197+
```typescript
198+
export class UpdateCatDto extends PartialType(CreateCatDto) {}
199+
```
200+
201+
> info **Hint** The `PartialType()` function is imported from the `@nestjs/swagger` package.
202+
203+
The `PickType()` function constructs a new type (class) by picking a set of properties from an input type. For example, suppose we start with a type like:
204+
205+
```typescript
206+
import { ApiProperty } from '@nestjs/swagger';
207+
208+
export class CreateCatDto {
209+
@ApiProperty()
210+
name: string;
211+
212+
@ApiProperty()
213+
age: number;
214+
215+
@ApiProperty()
216+
breed: string;
217+
}
218+
```
219+
220+
We can pick a set of properties from this class using the `PickType()` utility function:
221+
222+
```typescript
223+
export class UpdateCatAgeDto extends PickType(CreateCatDto, ['age']) {}
224+
```
225+
226+
> info **Hint** The `PickType()` function is imported from the `@nestjs/swagger` package.
227+
228+
The `OmitType()` function constructs a type by picking all properties from an input type and then removing a particular set of keys. For example, suppose we start with a type like:
229+
230+
```typescript
231+
import { ApiProperty } from '@nestjs/swagger';
232+
233+
export class CreateCatDto {
234+
@ApiProperty()
235+
name: string;
236+
237+
@ApiProperty()
238+
age: number;
239+
240+
@ApiProperty()
241+
breed: string;
242+
}
243+
```
244+
245+
We can generate a derived type that has every property **except** `name` as shown below. In this construct, the second argument to `OmitType` is an array of property names.
246+
247+
```typescript
248+
export class UpdateCatDto extends OmitType(CreateCatDto, ['name']) {}
249+
```
250+
251+
> info **Hint** The `OmitType()` function is imported from the `@nestjs/swagger` package.
252+
253+
The type mapping utility functions are composable. For example, the following will produce a type (class) that has all of the properties of the `CreateCatDto` type except for `name`, and those properties will be set to optional:
254+
255+
```typescript
256+
export class UpdateCatDto extends PartialType(
257+
OmitType(CreateCatDto, ['name']),
258+
) {}
259+
```
260+
129261
#### Enums
130262

131263
To identify an `enum`, we must manually set the `enum` property on the `@ApiProperty` with an array of values.
@@ -163,24 +295,20 @@ With `isArray` set to **true**, the `enum` can be selected as a **multi-select**
163295
By default, the `enum` property will add a raw definition of [Enum](https://swagger.io/docs/specification/data-models/enums/) on the `parameter`.
164296

165297
```yaml
166-
CatDetail:
167-
type: 'object'
168-
properties:
169-
...
170-
- breed:
171-
type: 'string'
172-
enum:
173-
- Persian
174-
- Tabby
175-
- Siamese
298+
- breed:
299+
type: 'string'
300+
enum:
301+
- Persian
302+
- Tabby
303+
- Siamese
176304
```
177305
178-
The above specification works fine for most cases. However, if you are utilizing a tool that takes the specification as **input** and generates **client-side** code, you might run into a problem with the generated code containing duplicated `enums`. Consider the following code snippet:
306+
The above specification works fine for most cases. However, if you are utilizing a tool that takes the specification as **input** and generates **client-side** code, you might run into a problem with the generated code containing duplicated `enums`. Consider the following code snippet:
179307

180308
```typescript
181309
// generated client-side code
182310
export class CatDetail {
183-
breed: CatDetailEnum;
311+
breed: CatDetailEnum;
184312
}
185313
186314
export class CatInformation {
@@ -190,29 +318,29 @@ export class CatInformation {
190318
export enum CatDetailEnum {
191319
Persian = 'Persian',
192320
Tabby = 'Tabby',
193-
Siamese = 'Siamese'
321+
Siamese = 'Siamese',
194322
}
195323
196324
export enum CatInformationEnum {
197325
Persian = 'Persian',
198326
Tabby = 'Tabby',
199-
Siamese = 'Siamese'
327+
Siamese = 'Siamese',
200328
}
201329
```
202330

203331
> info **Hint** The above snippet is generated using a tool called [NSwag](https://github.com/RicoSuter/NSwag).
204332

205-
You can see that now you have two `enums` that are exactly the same.
333+
You can see that now you have two `enums` that are exactly the same.
206334
To address this issue, you can pass an `enumName` next to `enum` property in your decorator.
207335

208336
```typescript
209337
export class CatDetail {
210-
@ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
211-
breed: CatBreed;
338+
@ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
339+
breed: CatBreed;
212340
}
213341
```
214342

215-
`enumName` enables `nestjs/swagger` to turn `CatBreed` into its own `schema` which in turns makes `CatBreed` reusable. The specification will look like the following:
343+
The `enumName` property enables `@nestjs/swagger` to turn `CatBreed` into its own `schema` which in turns makes `CatBreed` enum reusable. The specification will look like the following:
216344

217345
```yaml
218346
CatDetail:
@@ -232,47 +360,6 @@ CatBreed:
232360

233361
> info **Hint** Any **decorator** that takes `enum` as a property will also take `enumName`.
234362

235-
#### Arrays
236-
237-
When the property is an array, we must manually indicate the array type as shown below:
238-
239-
```typescript
240-
@ApiProperty({ type: [String] })
241-
names: string[];
242-
```
243-
244-
> info **Hint** Consider using the Swagger plugin (see [Plugin](/recipes/swagger#plugin) section) which will automatically detect arrays.
245-
246-
Either include the type as the first element of an array (as shown above) or set the `isArray` property to `true`.
247-
248-
<app-banner-enterprise></app-banner-enterprise>
249-
250-
#### Circular dependencies
251-
252-
When you have circular dependencies between classes, use a lazy function to provide the `SwaggerModule` with type information:
253-
254-
```typescript
255-
@ApiProperty({ type: () => Node })
256-
node: Node;
257-
```
258-
259-
> info **Hint** Consider using the Swagger plugin (see [Plugin](/recipes/swagger#plugin) section) which will automatically detect circular dependencies.
260-
261-
#### Generics and interfaces
262-
263-
Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, `SwaggerModule` may not be able to properly generate model definitions at runtime. For instance, below code won't be correctly inspected by the Swagger module:
264-
265-
```typescript
266-
createBulk(@Body() usersDto: CreateUserDto[])
267-
```
268-
269-
In order to overcome this limitation, you can set the type explicitly:
270-
271-
```typescript
272-
@ApiBody({ type: [CreateUserDto] })
273-
createBulk(@Body() usersDto: CreateUserDto[])
274-
```
275-
276363
#### Raw definitions
277364

278365
In some specific scenarios (e.g. deeply nested arrays, matrices), you may want to describe your type by hand.

0 commit comments

Comments
 (0)