Pourquoi Migrer de Express vers NestJS ?
La transition de Express à NestJS peut offrir plusieurs avantages aux développeurs qui cherchent à créer des applications robustes, maintenables et modulaires. En effet, NestJS est une plateforme basée sur le design de modèles Microservices, ce qui lui permet d'offrir un architecture plus propre et scalable. Plus précisément, NestJS est construit sur les dernières technologies du langage TypeScript, ce qui offre des avantages en termes de type-checking et de développement assuré.
Un cas concret pour migrer de Express vers NestJS pourrait être le développement d'une application backend complexe avec une architecture Microservices. Dans ce cas, NestJS permet de créer facilement ces services et d'interconnecter entre eux, tout en offrant des fonctionnalités comme les injections de dépendances, les tests unitaires faciles à mettre en place et la gestion efficace des erreurs.
Prerequis
- Connaissance approfondie de Node.js et Express
- Familiarité avec TypeScript
- Compréhension du concept d'architecture Microservices
- Installation de Node.js (v14.x ou plus tard)
- Installation de npm (Node Package Manager)
Concepts fondamentaux
Architecture Microservices
NestJS est conçu pour être une plateforme pour développer des applications basées sur le design de modèles Microservices. Les microservices sont des services autonomes qui communiquent entre eux via des APIs REST ou gRPC. Chaque service peut être développé, déployé et échelle indépendamment.
// Structure d'un microservice NestJS
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
Injections de Dépendances
L'injection de dépendances est un pattern qui permet d'injecter des objets nécessaires dans les classes. Cela rend le code plus modulaire et facile à tester.
// Exemple d'injection de dépendances
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
async findAll(): Promise<User[]> {
return this.userRepository.find();
}
}
Middleware
Middleware est une fonction qui a accès à l'objet de requête (req), à l'objet de réponse (res) et à la chaîne suivante dans le cycle de traitement des requêtes. Elle peut effectuer des tâches telles que la validation des données, la gestion des erreurs ou la journalisation.
// Exemple d'un middleware personnalisé
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
}
}
DTO (Data Transfer Object)
Les DTO sont des objets utilisés pour transférer des données entre les différents composants de l'application. Ils permettent de spécifier la structure des données et de valider ces données avant leur utilisation.
// Exemple d'un DTO
import { IsNotEmpty, IsEmail } from 'class-validator';
export class CreateUserDto {
@IsNotEmpty()
username: string;
@IsNotEmpty()
@IsEmail()
email: string;
}
Mise en pratique : projet fil rouge
Nous allons créer un gestionnaire de tâches basé sur NestJS. Le projet comprendra les fonctionnalités suivantes :
- Création d'une tâche
- Récupération de toutes les tâches
- Suppression d'une tâche
Étape 1 : Initialisation du Projet
Commencez par créer un nouveau projet NestJS.
npm install -g @nestjs/cli
nest new task-manager
cd task-manager
Étape 2 : Création des entités et modèles
Créez une entité Task pour représenter les tâches dans la base de données.
// src/task/entities/task.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Task {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', length: 255 })
title: string;
@Column({ type: 'text' })
description: string;
@Column({ type: 'boolean', default: false })
isCompleted: boolean;
}
Créez un modèle TaskService pour gérer les opérations CRUD sur les tâches.
// src/task/task.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Task } from './entities/task.entity';
@Injectable()
export class TaskService {
constructor(
@InjectRepository(Task)
private readonly taskRepository: Repository<Task>,
) {}
async findAll(): Promise<Task[]> {
return this.taskRepository.find();
}
async create(taskDto: Task): Promise<Task> {
const newTask = this.taskRepository.create(taskDto);
return this.taskRepository.save(newTask);
}
async delete(id: number): Promise<void> {
await this.taskRepository.delete(id);
}
}
Étape 3 : Création des contrôleurs
Créez un contrôleur TaskController pour gérer les requêtes HTTP.
// src/task/task.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { TaskService } from './task.service';
import { CreateTaskDto } from './dto/create-task.dto';
@Controller('tasks')
export class TaskController {
constructor(private readonly taskService: TaskService) {}
@Post()
async create(@Body() createTaskDto: CreateTaskDto): Promise<Task> {
return this.taskService.create(createTaskDto);
}
@Get()
async findAll(): Promise<Task[]> {
return this.taskService.findAll();
}
@Delete(':id')
async delete(@Param('id') id: number): Promise<void> {
await this.taskService.delete(id);
}
}
Étape 4 : Configuration des routes
Ajoutez les routes dans app.module.ts pour lier le contrôleur aux routes HTTP.
// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TaskController } from './task/task.controller';
import { TaskService } from './task/task.service';
import { Task } from './task/entities/task.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: 'tasks.db',
entities: [Task],
synchronize: true,
}),
TypeOrmModule.forFeature([Task]),
],
controllers: [TaskController],
providers: [TaskService],
})
export class AppModule {}
Étape 5 : Exécution du projet
Exécutez le projet et testez les routes.
npm run start:dev
Allez sur http://localhost:3000/tasks pour voir les tâches, http://localhost:3000/tasks pour créer une nouvelle tâche et http://localhost:3000/tasks/:id pour supprimer une tâche.
Erreurs frequentes et debugging
Erreur : "Cannot find module 'typeorm'"
// code_incorrect = import { Repository } from 'typeorm';Correction :
// code_correct = import { Repository } from 'typeorm'; npm install typeorm reflect-metadata --save npm install @nestjs/typeorm --save**Erreur : "Type 'CreateTaskDto' is not assignable to type 'Partial
'" // code_incorrect = return this.taskService.create(createTaskDto);Correction :
// code_correct = const newTask = this.taskRepository.create(createTaskDto); // return this.taskRepository.save(newTask);**Erreur : "Cannot find name 'Promise'"
// code_incorrect = async findAll(): Promise<Task[]> {Correction :
// code_correct = async findAll(): Promise<Task[]> {
Pour aller plus loin
- Documentation NestJS officielle : https://docs.nestjs.com/
- TypeORM pour la gestion des bases de données : https://typeorm.io/
- Présentation des Microservices avec NestJS : https://nestjs.io/microservices/
Défi pratique
Créez un application API de blog simple avec les fonctionnalités suivantes :
- Création d'un article
- Récupération de tous les articles
- Suppression d'un article
Utilisez NestJS pour développer l'API et TypeORM pour la gestion de la base de données SQLite.