You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/openapi/operations.md
+174Lines changed: 174 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -152,3 +152,177 @@ To add an Extension to a request use the `@ApiExtension()` decorator. The extens
152
152
```typescript
153
153
@ApiExtension('x-foo', { hello: 'world' })
154
154
```
155
+
156
+
#### Advanced: Generics ApiResponse
157
+
158
+
With the ability to provide **Raw Definition**, we can provide **Generics** schema for SwaggerUI. Assume we have the following *generics DTO*
159
+
160
+
```ts
161
+
exportclassPaginatedDto<TData> {
162
+
@ApiProperty()
163
+
total:number;
164
+
165
+
@ApiProperty()
166
+
limit:number;
167
+
168
+
@ApiProperty()
169
+
offset:number;
170
+
171
+
results:TData[];
172
+
}
173
+
```
174
+
175
+
We skip decorating `results` because we will be providing **Raw Definition** for it later. Now, let's assume we have the following `CatDto`
176
+
177
+
```ts
178
+
exportclassCatDto {
179
+
@ApiProperty()
180
+
name:string;
181
+
182
+
@ApiProperty()
183
+
age:number;
184
+
185
+
@ApiProperty()
186
+
breed:string;
187
+
}
188
+
```
189
+
190
+
Now, we can start providing a `PaginatedDto<CatDto>` on `CatController`
191
+
192
+
```ts
193
+
@Controller(...)
194
+
exportclassCatController {
195
+
196
+
@Get()
197
+
@ApiOkResponse({
198
+
schema: {
199
+
allOf: [
200
+
{ $ref: getSchemaPath(PaginatedDto) },
201
+
{
202
+
properties: {
203
+
results: {
204
+
type: 'array',
205
+
items: { $ref: getSchemaPath(CatDto) },
206
+
},
207
+
},
208
+
},
209
+
],
210
+
},
211
+
})
212
+
async get(...):Promise<PaginatedDto<CatDto>> {
213
+
...
214
+
}
215
+
}
216
+
```
217
+
218
+
We are not done. `PaginatedDto` isn't part of any controller by itself so `SwaggerModule` won't be able to scan it
219
+
during initialization. But, `nestjs/swagger` provides an `ApiExtraModels()` decorator for such cases.
220
+
221
+
```ts
222
+
@Controller(...)
223
+
@ApiExtraModels(PaginatedDto)
224
+
exportclassCatController {
225
+
...
226
+
}
227
+
```
228
+
229
+
> info **Hint** You only need to use `ApiExtraModels` for a specific `Dto` once so find a place where it makes sense for you to do so.
230
+
231
+
-`getSchemaPath()` returns the OpenAPI Schema path from within the OpenAPI Spec File that `ApiExtraModels` helps `nestjs/swagger` generates,
232
+
or `nestjs/swagger` is able to scan automatically.
233
+
-`allOf` is a concept that OpenAPI 3 has to cover Inheritance use-cases.
234
+
235
+
In this case, we tell SwaggerUI that this response will have **allOf**`PaginatedDto` and the `results` property will be of type array and each item will be of type `CatDto`.
236
+
If you run the SwaggerUI now, you'd see the generated `swagger.json` for this specific endpoint like the following:
237
+
238
+
```json
239
+
responses": {
240
+
"200": {
241
+
"description": "",
242
+
"content": {
243
+
"application/json": {
244
+
"schema": {
245
+
"allOf": [
246
+
{
247
+
"$ref": "#/components/schemas/PaginatedDto"
248
+
},
249
+
{
250
+
"properties": {
251
+
"results": {
252
+
"$ref": "#/components/schemas/CatDto"
253
+
}
254
+
}
255
+
}
256
+
]
257
+
}
258
+
}
259
+
}
260
+
}
261
+
}
262
+
```
263
+
264
+
Now that we know it works, we can create a custom decorator for `PaginatedDto` as follow:
then we can use `ApiPaginatedResponse` on our endpoint:
289
+
290
+
```ts
291
+
@Get()
292
+
@ApiPaginatedResponse(CatDto)
293
+
asyncget(): Promise<PaginatedDto<CatDto>> {}
294
+
```
295
+
296
+
You can modify `ApiPaginatedResponse` as you see fit, maybe make it more generics to handle non-array `results` or maybe different property name than `results`.
297
+
Knowing the capabilities of `nestjs/swagger` APIs, you can totally go wild with it and make sure your OpenAPI Spec is correct and covered.
298
+
299
+
For client generation tools, this approach poses an ambiguity in how this `PaginatedResponse<TModel>` is being generated for the client. The following snippet is an example of a client generator result of the above **GET** request:
As you can see, the **Return Type** here is ambiguous. To workaround this issue, you can add a `title` property to the `schema` for `ApiPaginatedResponse`:
0 commit comments