← Voir tous les articlesGrégory TAUDINGrégory TAUDIN - 14 juin 2022

NestJS : Les DTO et Automapper

NestJS logo

L'API est terminée et fonctionne correctement, pourtant un des fondamentaux d'une bonne API est de découpler les entités provenant de la base et ceux qui sont retournés sous forme de résultats. En effet, le but d'une API est de servir une ressource. Or cette ressource n'a pas forcément besoin de tous les champs de notre objet entité mais parfois juste d'un sous-ensemble pour coller parfaitement aux besoins UI ou en termes de performance : on pense notamment aux clés étrangères nous permettant de ramener des données d'une autre table sous forme d'objets imbriqués.

Ce concept est traduit par les objets de type DTO.

Un objet de transfert de données (data transfer object ou DTO en anglais) est un patron de conception utilisé dans les architectures logicielles objets. Son but est de simplifier les transferts de données entre les sous-systèmes d'une application logicielle. Les objets de transfert de données sont souvent utilisés en conjonction des objets d'accès aux données.

On rejoint le concept d'interface avec les contrats de données. BREF tout un programme. Nous allons nous arrêter là pour partir à la découverte d'Automapper.

Automapper permet de mapper un objet vers un autre et intègre des fonctionnalités vraiment puissantes, par exemple, le fait de pouvoir créer des propriétés calculées à la volée au moment du mapping ou de ne prendre que certaines propriétés.

Installation

Installons la librairie :

> npm i @automapper/core
> npm i @automapper/classes
> npm i @automapper/nestjs
> npm i @automapper/types

Configurons l'importation au sein de notre module partagé.

AutomapperModule.forRoot({
strategyInitializer: classes(),
}),

Nous allons utiliser le mapping entre classes. Pour créer nos objets DTO, nous avons besoin :

  • Un objet pour représenter un item,
  • Un objet pour la création d'un item,
  • Un objet pour la mise à jour d'un item.

Dans « shared/src » créons un nouveau répertoire « dto » et créons nos objets DTO.

Github code
Github code
Github code

Nous devons aussi modifier notre entité pour signaler à Automapper quelles sont les propriétés à utiliser avec le décorateur @Automap().

Github code

Automapper nous permet de créer un fichier de profile. C'est dans celui-ci que nous allons pouvoir créer le mapping entre nos classes. Dans « shared/src » créons un nouveau répertoire « profile ».

Github code

Nous créons 3 types de mapping pour chaque DTO dont nous avons besoin.

Remarquez le sens de chaque mapping :

  • Pour la lecture nous allons de l'entité vers le DTO car nous partons de la base vers l'extérieur.
  • Pour l'écriture et la mise à jour, nous allons de l'extérieur vers la base donc à l'opposé, nous partons du DTO pour aller vers l'entité. Remarquez que pour l'opération d'écriture nous avons rajouté le fait que la mise à jour de l'ID est ignorée.

Nous n'avons plus qu'à utiliser le profil en tant que provider de notre librairie. La couche applicative ne manipulera plus les entités et n'aura plus besoin que de connaître ces DTO.

Le découplage est effectif !

Comment utiliser le mapping automatique ?

Retournons dans notre service partagé. Dans le constructeur, injectons notre mapper :

Github code

Nous devons utiliser certaines fonctions du mappeur pour mapper les résultats et mettre à jour la définition de nos fonctions !

this.classMapper.map(item, InventoryItemCreateDTO, InventoryItem);

Nous mappons un objet de type "InventoryItemCreateDTO" à un objet de type "InventoryItem" (vous pouvez également utiliser la fonction "mapAsync" si vous devez utiliser un processus asynchrone).

this.classMapper.mapArrayAsync(await
this.inventoryItemRepository.find(), InventoryItem,
InventoryItemReadDTO);

Avec "mapArrayAsync" nous mappons un tableau de type "InventoryItem" à un type de tableau "InventoryItemReadDTO". Simple et efficace !

Il reste une dernière étape : mettre à jour la définition des fonctions des contrôleurs pour renvoyer nos objets DTO au lieu de l'entité "InventoryItem".

Github code
Github code

et bien sûr, mettre à jour tous les tests !

Mission complete!

Notre découplage est terminé, il est temps de documenter notre API ! Nous pouvons penser à la prochaine étape : La documentation ultime d'API avec Swagger.