Aller au contenu

Maîtriser NestJS : comprendre Services, Modules et Contrôleurs

3 septembre 2023 · 6 min read · Read in English

Sommaire

Introduction

Dans un précédent article, on t'a introduit à NestJS, un framework moderne basé sur TypeScript et Node.js, qui apporte structure et efficacité à tes projets backend. Dans cette série, on plonge plus profondément dans les fonctionnalités et concepts clés de NestJS. Tu auras une compréhension complète des modules, contrôleurs, routes, providers, services, gestion des exceptions et validation, à travers un projet qui consistera à implémenter le CRUD (Create, Read, Update, Delete) de produits d'un catalogue e-commerce.

J'ai créé un dépôt GitHub pour cette série, accessible à l' adresse suivante.

Ressource CRUD

Puisqu'on s'apprête à créer une ressource Product, le plus direct est de lancer une seule commande qui va générer une nouvelle ressource CRUD : un module, un contrôleur pour définir les routes CRUD, un service pour implémenter et isoler la logique métier, une classe/interface entité pour représenter la forme des données, et des DTO pour définir comment les données seront échangées sur le réseau.

Créons notre première ressource CRUD avec la commande CLI suivante :

bash
 nest g resource products
Création d'une ressource Nest

Sur la capture suivante, remarque qu'on utilise REST API comme couche de transport et qu'on répond Y quand on nous demande si on veut générer les points d'entrée CRUD.

Si tout se passe bien, tu remarqueras un nouveau dossier nommé products et que ton fichier app.module.ts a aussi changé. On va explorer tout ça dans les sections suivantes.

Création d'une ressource Nest

Entité

Le rôle d'une entité dans NestJS est de définir la structure et le schéma de tes modèles de données, principalement pour les interactions avec la base de données. Dans un projet réel, cette classe sera manipulée via un ORM comme Prisma, MikroORM, TypeORM…, mais pour l'instant on n'entre pas dans ces détails.

Voici à quoi ressemble notre entité produit :

product.entity.ts
export class Product {
  id: number;
  name: string;
  description: string;
  category: string;
  price: number;
  image: string;
}

Service

Dans un projet NestJS, les services permettent de séparer la logique de ton application en unités réutilisables et testables. Cette séparation des préoccupations favorise la maintenabilité du code et facilite la collaboration sur des projets plus larges.

product.service.ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Product } from './entities/product.entity';

@Injectable()
export class ProductsService {
  private lastProductId = 0;
  private products = [];

  create(createProductDto: CreateProductDto): Product {
    const newProduct = { id: ++this.lastProductId, ...createProductDto };
    this.products.push(newProduct);
    return newProduct;
  }

  findAll(): Product[] {
    return this.products;
  }

  findOne(id: number) {
    const product = this.products.find((product) => product.id === id);
    if (product) {
      return product;
    }
    throw new HttpException('Product not found', HttpStatus.NOT_FOUND);
  }

  update(id: number, updateProductDto: UpdateProductDto) {
    const productIndex = this.products.findIndex(
      (product) => product.id === id,
    );

    if (productIndex > -1) {
      this.products[productIndex] = updateProductDto;
      return updateProductDto;
    }
    throw new HttpException('Product not found', HttpStatus.NOT_FOUND);
  }

  remove(id: number) {
    const productIndex = this.products.findIndex(
      (product) => product.id === id,
    );

    if (productIndex > -1) {
      this.products.splice(productIndex, 1);
    }
    throw new HttpException('Product not found', HttpStatus.NOT_FOUND);
  }
}

Note que notre classe service implémente les cinq méthodes CRUD :

  • create : créer une nouvelle ressource produit
  • findAll : récupérer tous les produits
  • findOne : récupérer un produit par son ID
  • update : mettre à jour une ressource produit
  • delete : supprimer un produit

Data Transfer Objects

En substance, les DTO aident à contrôler et valider les données lors de la création ou mise à jour de ressources dans une application NestJS, en garantissant qu'elles respectent des règles et contraintes spécifiques.

Create DTO

Ils définissent la structure des données entrantes à la création d'une ressource. Cela aide à valider et formater les données avant leur traitement.

create-product.dto.ts
export class CreateProductDto {
  name: string;
  description: string;
  category: string;
  price: number;
  image: string;
}
Update DTO

Ils spécifient les champs autorisés à la mise à jour d'une ressource, garantissant que seules des données valides peuvent être modifiées — renforçant intégrité et sécurité.

update-product.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateProductDto } from './create-product.dto';

export class UpdateProductDto extends PartialType(CreateProductDto) {
  id: number;
}

Contrôleur

Les contrôleurs sont responsables de recevoir les requêtes entrantes et de répondre au client. Leur but principal est de recevoir les requêtes spécifiques pour l'application. Chaque contrôleur a souvent plus d'une route, et des routes distinctes peuvent réaliser des actions différentes.

Regardons à quoi ressemble notre contrôleur produit :

product.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { ProductsService } from './products.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';

@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {
  }

  @Post()
  create(@Body() createProductDto: CreateProductDto) {
    return this.productsService.create(createProductDto);
  }

  @Get()
  findAll() {
    return this.productsService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.productsService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateProductDto: UpdateProductDto) {
    return this.productsService.update(+id, updateProductDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.productsService.remove(+id);
  }
}

La première chose à noter : NestJS fait un usage intensif des décorateurs. Le décorateur @Controller() sert à indiquer qu'une classe est un contrôleur. Dans l'exemple ci-dessus, on a utilisé @Controller() avec un paramètre products qui représente le chemin de la route. Note que si tu ne fournis pas de valeur en paramètre, le contrôleur est accessible depuis l'hôte sans avoir besoin de fournir un nom de chemin.

Routing

Les décorateurs de méthode HTTP @Get(), @Post(), @Delete() et @Put() placés devant les méthodes de la classe contrôleur indiquent à NestJS de créer un handler pour le endpoint équivalent. Le endpoint correspond à la méthode HTTP et au chemin de la route.

Body de requête et Query Param

On a besoin d'accéder au contenu d'une requête lors du traitement des POST et PUT dans le contrôleur ci-dessus. NestJS inclut un décorateur @Body() qui permet d'accéder facilement au corps de la requête HTTP. On a déjà introduit le concept de DTO. Il aide à contrôler et valider les données lors de la création ou mise à jour de ressources.

Parallèlement, remarque le décorateur @Param('id') qui indique à NestJS que notre chemin contient un paramètre nommé id, qu'il faut fournir au moment de la requête.

Module

Un module est une classe annotée par le décorateur @Module(). Nest utilise les métadonnées fournies par @Module() pour organiser la structure de l'application.

Chaque application contient au moins un module, appelé module racine. Nest construit le graphe applicatif depuis le module racine, qui est la structure de données interne utilisée par Nest pour résoudre les interactions et dépendances entre modules et providers. Même si en théorie de très petits programmes peuvent ne contenir que le module racine, ce n'est pas la norme.

Regardons à quoi ressemble le module produit :

product.module.ts
import { Module } from '@nestjs/common';
import { ProductsService } from './products.service';
import { ProductsController } from './products.controller';

@Module({
  controllers: [ProductsController],
  providers: [ProductsService],
})
export class ProductsModule {
}

Le décorateur @Module() prend un objet unique dont les propriétés décrivent le module :

  • controllers : l'ensemble des contrôleurs définis dans ce module et qui doivent être instanciés.
  • providers : les providers qui seront instanciés par l'injecteur Nest et qui peuvent être partagés au moins à l'intérieur de ce module.
  • imports : la liste des modules importés qui exportent les providers nécessaires à ce module.
  • exports : le sous-ensemble de providers fournis par ce module et qui doivent être disponibles dans d'autres modules qui importent celui-ci.

Enfin, jetons un œil au fichier app.module.ts :

app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProductsModule } from './products/products.module';

@Module({
  imports: [ProductsModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
}

Et maintenant ? Démarrer le serveur de développement, peut-être ? Allons-y !

Démarrage du serveur NestJS

Voilà, tout est maintenant en place pour gérer un petit CRUD de produits pour un catalogue e-commerce. Je partage la collection Postman pour tester les endpoints dans ton environnement.

Résumé

On vient juste de gratter la surface de NestJS. On comprend désormais ce qu'est un contrôleur et comment gérer du routing basique dans notre application. On a également brièvement abordé les services et les modules. Dans les prochaines parties de cette série, on consacrera un temps significatif à étudier la structure d'application en NestJS.

Toutes les informations présentées ci-dessus ne sont que la partie émergée de l'iceberg NestJS. J'espère t'avoir convaincu que ce framework vaut la peine d'être exploré, parce qu'il apporte beaucoup de valeur. Il y a énormément à dire sur les fonctionnalités de Nest, comme la gestion propre des erreurs et l'injection de dépendances. On abordera aussi la base PostgreSQL et son usage via un ORM, et bien sûr on parlera d'authentification et d'autorisation avec NestJS.

À suivre !

S'abonner aux prochains articles

Recevez les nouveaux articles par e-mail. Pas de spam, désinscription à tout moment.

Propulsé par Buttondown.

Related posts

© 2026 < Denis AKPAGNONITE /> | N1BBzerLZXT