Passer au contenu

Opérations

En termes d’OpenAPI, les chemins sont des points de terminaison (ressources), tels que /users ou /reports/summary, que votre API expose, et les opérations sont les méthodes HTTP utilisées pour manipuler ces chemins, telles que GET, POST ou DELETE.

Tags

Pour attacher un contrôleur à un tag spécifique, utilisez le @ApiTags(...tags) décorateur.

Exemple d'utilisation des Tags
@ApiTags('cats')
@Controller('cats')
export class CatsController {
}

En-têtes

Pour définir des en-têtes personnalisés attendus dans la requête, utilisez @ApiHeader().

Exemple d'utilisation des En-têtes
@ApiHeader({
name: 'X-MyHeader',
description: 'En-tête personnalisé',
})
@Controller('cats')
export class CatsController {
}

Réponses

Pour définir une réponse HTTP personnalisée, utilisez le @ApiResponse() décorateur.

Exemple d'utilisation des Réponses
@Post()
@ApiResponse({ status: 201, description: 'Le dossier a été créé avec succès.' })
@ApiResponse({ status: 403, description: 'Interdit.' })
async create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}

Nest fournit un ensemble de décorateurs de réponse API abrégés qui héritent du décorateur @ApiResponse :

  • @ApiOkResponse()
  • @ApiCreatedResponse()
  • @ApiAcceptedResponse()
  • @ApiNoContentResponse()
  • @ApiMovedPermanentlyResponse()
  • @ApiFoundResponse()
  • @ApiBadRequestResponse()
  • @ApiUnauthorizedResponse()
  • @ApiNotFoundResponse()
  • @ApiForbiddenResponse()
  • @ApiMethodNotAllowedResponse()
  • @ApiNotAcceptableResponse()
  • @ApiRequestTimeoutResponse()
  • @ApiConflictResponse()
  • @ApiPreconditionFailedResponse()
  • @ApiTooManyRequestsResponse()
  • @ApiGoneResponse()
  • @ApiPayloadTooLargeResponse()
  • @ApiUnsupportedMediaTypeResponse()
  • @ApiUnprocessableEntityResponse()
  • @ApiInternalServerErrorResponse()
  • @ApiNotImplementedResponse()
  • @ApiBadGatewayResponse()
  • @ApiServiceUnavailableResponse()
  • @ApiGatewayTimeoutResponse()
  • @ApiDefaultResponse()
Exemple des Réponses Abrégées
@Post()
@ApiCreatedResponse({ description: 'Le dossier a été créé avec succès.' })
@ApiForbiddenResponse({ description: 'Interdit.' })
async create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}

Pour spécifier un modèle de retour pour une requête, nous devons créer une classe et annoter toutes les propriétés avec le @ApiProperty() décorateur.

Modèle de Cat
export class Cat {
@ApiProperty()
id: number;
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}

Le modèle Cat peut ensuite être utilisé en combinaison avec la propriété type du décorateur de réponse.

Exemple d'utilisation du Modèle de Cat
@ApiTags('cats')
@Controller('cats')
export class CatsController {
@Post()
@ApiCreatedResponse({
description: 'Le dossier a été créé avec succès.',
type: Cat,
})
async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
return this.catsService.create(createCatDto);
}
}

Ouvrons le navigateur et vérifions le modèle Cat généré.

Response Type

Téléchargement de Fichiers

Vous pouvez activer le téléchargement de fichiers pour une méthode spécifique avec le décorateur @ApiBody associé à @ApiConsumes(). Voici un exemple complet utilisant la technique de Téléchargement de Fichiers.

Exemple de Téléchargement de Fichiers
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'Liste de chats',
type: FileUploadDto,
})
uploadFile(@UploadedFile() file) {
}

FileUploadDto est défini comme suit :

Définition de FileUploadDto
class FileUploadDto {
@ApiProperty({ type: 'string', format: 'binary' })
file: any;
}

Pour gérer le téléchargement de plusieurs fichiers, vous pouvez définir FilesUploadDto comme suit :

Définition de FilesUploadDto
class FilesUploadDto {
@ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } })
files: any[];
}

Extensions

Pour ajouter une extension à une requête, utilisez le décorateur @ApiExtension(). Le nom de l’extension doit être préfixé par x-.

Exemple d'utilisation des Extensions
@ApiExtension('x-foo', { hello: 'world' })

Avancé : Generic ApiResponse

Avec la possibilité de fournir des Définitions Brutes, nous pouvons définir un schéma générique pour Swagger UI.

Voici un exemple de DTO :

Définition de PaginatedDto
export class PaginatedDto<TData> {
@ApiProperty()
total: number;
@ApiProperty()
limit: number;
@ApiProperty()
offset: number;
results: TData[];
}

Nous évitons de décorer results car nous fournirons une définition brute pour cela plus tard. Maintenant, définissons un autre DTO et nommons-le CatDto comme suit :

Définition de CatDto
export class CatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}

Avec cela en place, nous pouvons définir une réponse PaginatedDto<CatDto> comme suit :

Exemple de réponse paginée
@ApiOkResponse({
schema: {
allOf: [
{ $ref: getSchemaPath(PaginatedDto) },
{
properties: {
results: {
type: 'array',
items: { $ref: getSchemaPath(CatDto) }
}
}
}
]
}
})
async findAll(): Promise<PaginatedDto<CatDto>> {
}

Dans cet exemple, nous spécifions que la réponse aura le schéma PaginatedDto et que la propriété results sera de type Array<CatDto>.

Enfin, puisque PaginatedDto n’est pas directement référencé par aucun contrôleur, le SwaggerModule ne sera pas en mesure de générer une définition de modèle correspondante pour le moment. Dans ce cas, nous devons l’ajouter comme un Modèle Supplémentaire. Par exemple, nous pouvons utiliser le décorateur @ApiExtraModels() au niveau du contrôleur :

Modèle Supplémentaire dans le Contrôleur
@Controller('cats')
@ApiExtraModels(PaginatedDto)
export class CatsController {
}

Si vous exécutez Swagger maintenant, le swagger.json généré pour ce point de terminaison spécifique devrait avoir la réponse définie.

Exemple de réponse swagger.json
{
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"allOf": [
{ "$ref": "#/components/schemas/PaginatedDto" },
{
"properties": {
"results": { "$ref": "#/components/schemas/CatDto" }
}
}
]
}
}
}
}
}
}

Pour le rendre réutilisable, nous pouvons créer un décorateur personnalisé pour PaginatedDto :

Création d'un décorateur personnalisé
export const ApiPaginatedResponse = <TModel extends Type<any>>(model: TModel) => {
return applyDecorators(
ApiExtraModels(PaginatedDto, model),
ApiOkResponse({
schema: {
allOf: [
{ $ref: getSchemaPath(PaginatedDto) },
{
properties: {
results: {
type: 'array',
items: { $ref: getSchemaPath(model) }
}
}
}
]
}
})
);
}

Avec cela en place, nous pouvons utiliser le décorateur personnalisé @ApiPaginatedResponse() sur notre point de terminaison :

Utilisation du décorateur personnalisé
@ApiPaginatedResponse(CatDto)
async findAll(): Promise<PaginatedDto<CatDto>> {
}

Pour les outils de génération de clients, cette approche pose une ambiguïté quant à la façon dont la réponse PaginatedResponse<TModel> est générée pour le client. Le code suivant est un exemple de résultat d’un générateur de client pour le point de terminaison ci-dessus.

Exemple de code généré pour le client
findAll(): Observable<{ total: number; limit: number; offset: number; results: CatDto[] }> {
}

Comme vous pouvez le voir, le Type de Retour ici est ambigu. Pour contourner ce problème, vous pouvez ajouter une propriété title au schema pour ApiPaginatedResponse :

Ajout d'une propriété title au schéma
export const ApiPaginatedResponse = <TModel extends Type<any>>(model: TModel) => {
return applyDecorators(
ApiOkResponse({
schema: {
title: `PaginatedResponseOf${model.name}`,
allOf: [
// ...
],
},
})
);
}

Maintenant, le résultat de l’outil de génération de client deviendra :

Exemple de code généré pour le client mis à jour
findAll(): Observable<PaginatedResponseOfCatDto> {
}