Pourquoi Optimiser les performances NestJS ?
Dans un monde où la performance est une priorité, optimiser les performances de votre application NestJS peut faire toute la différence. Que vous travailliez sur une petite API d'entreprise ou une grande application web monolithique, des milliers d'utilisateurs peuvent être impliqués et chaque milliseconde compte. Un exemple concret : une application de messagerie en temps réel doit pouvoir traiter des centaines de messages par seconde pour garantir une expérience utilisateur fluide.
Prerequis
- Connaissances en JavaScript/TypeScript
- Familiarité avec NestJS (Controllers, Services, Modules)
- Compréhension de la gestion des requêtes et réponses HTTP
- Connaissance des concepts asynchrones en JavaScript/TypeScript
Outils à installer :
- Node.js v14.x ou plus tard
- npm v6.x ou plus tard
- NestJS CLI (npm install -g @nestjs/cli)
- IDE ou éditeur de texte (VSCode recommandé)
Concepts fondamentaux
1. Asynchronisme avec async et await
La gestion des opérations asynchrones est cruciale pour les performances.
// ❌ Mauvais : Utilisation de callbacks
function getUser(userId, callback) {
setTimeout(() => {
const user = { id: userId, name: 'John' };
callback(user);
}, 1000);
}
getUser(1, (user) => {
console.log('User:', user);
});
typescript
// ✅ Correct : Utilisation de async/await
async function getUser(userId) {
return new Promise((resolve) => {
setTimeout(() => {
const user = { id: userId, name: 'John' };
resolve(user);
}, 1000);
});
}
(async () => {
try {
const user = await getUser(1);
console.log('User:', user);
} catch (error) {
console.error(error);
}
})();
2. Utilisation de @Injectable pour les services
Les services NestJS sont des composants qui contiennent la logique métier et peuvent être injectés dans les contrôleurs.
// src/user/user.service.ts
import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
@Injectable()
export class UserService {
private users: User[] = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
];
async findUserById(id: number): Promise<User> {
return this.users.find(user => user.id === id);
}
}
3. Utilisation de @Controller pour les contrôleurs
Les contrôleurs NestJS gèrent les requêtes HTTP et retournent les réponses.
// src/user/user.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get(':id')
async getUser(@Param('id') id: string): Promise<User> {
return this.userService.findUserById(parseInt(id));
}
}
4. Utilisation de @Module pour les modules
Les modules NestJS regroupent des contrôleurs, des services et d'autres composants.
// src/user/user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
Mise en pratique : projet fil rouge
Étape 1 : Création du projet NestJS
nest new task-manager
cd task-manager
npm install --save class-validator class-transformer @nestjs/typeorm typeorm pg
Étape 2 : Création d'une entité Task
// src/task/task.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Task {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 255 })
title: string;
@Column({ default: false })
completed: boolean;
}
Étape 3 : Création d'un service pour 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 './task.entity';
@Injectable()
export class TaskService {
constructor(
@InjectRepository(Task)
private readonly taskRepository: Repository<Task>,
) {}
async findAll(): Promise<Task[]> {
return this.taskRepository.find();
}
async findOne(id: number): Promise<Task> {
return this.taskRepository.findOne(id);
}
async create(task: Task): Promise<Task> {
return this.taskRepository.save(task);
}
async update(id: number, task: Task): Promise<Task> {
await this.taskRepository.update(id, task);
return this.taskRepository.findOne(id);
}
async delete(id: number): Promise<void> {
await this.taskRepository.delete(id);
}
}
Étape 4 : Création d'un contrôleur pour les tâches
// src/task/task.controller.ts
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { TaskService } from './task.service';
import { Task } from './task.entity';
@Controller('tasks')
export class TaskController {
constructor(private readonly taskService: TaskService) {}
@Get()
async findAll(): Promise<Task[]> {
return this.taskService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: number): Promise<Task> {
return this.taskService.findOne(id);
}
@Post()
async create(@Body() task: Task): Promise<Task> {
return this.taskService.create(task);
}
@Put(':id')
async update(@Param('id') id: number, @Body() task: Task): Promise<Task> {
return this.taskService.update(id, task);
}
@Delete(':id')
async delete(@Param('id') id: number): Promise<void> {
await this.taskService.delete(id);
}
}
Étape 5 : Configuration de la base de données
Modifier src/typeorm.config.ts :
// src/typeorm.config.ts
export default {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'your_username',
password: 'your_password',
database: 'task_manager',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
Étape 6 : Migrer le schéma de la base de données
npm install --save-dev typeorm-generator-cli pg
npx typeorm-generator -h localhost -d task_manager -u your_username -p your_password -o src/task/entities.ts
Erreurs fréquentes et debugging
1. Error: Cannot find module 'typeorm'
// ❌ Mauvais : Package non installé
import { Repository } from 'typeorm';
bash
npm install --save typeorm
2. TypeError: Cannot read property 'findOne' of undefined
// ❌ Mauvais : Injection de service incorrecte
constructor(private readonly taskService) {}
typescript
// ✅ Correct : Injection de service correcte
constructor(private readonly taskService: TaskService) {}
3. Error: No database connection is active
// ❌ Mauvais : Configuration de la base de données incorrecte
export default {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'wrong_username', // Erreur ici
password: 'your_password',
database: 'task_manager',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
typescript
// ✅ Correct : Configuration de la base de données correcte
export default {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'your_username', // Correction ici
password: 'your_password',
database: 'task_manager',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
};
Pour aller plus loin
1. Utilisation de @ValidationPipe pour la validation des données entrantes
2. Optimisation avec async/await et Promise.all
3. Utilisation de @InjectConnection pour la gestion des connexions
- Lien : https://orkhan.gitbook.io/typeorm/documentations/connection-manager#injectconnection-di-token
Défi pratique : Implémentez un système de pagination pour votre API de tâches. Cela implique d'ajouter des paramètres de requête pour la page et le nombre de résultats par page, puis de modifier les services et contrôleurs pour prendre en compte ces paramètres.
Ce tutoriel offre une compréhension approfondie des concepts clés de NestJS et montre comment les mettre en œuvre dans un projet réel. En suivant ces étapes, vous serez bien équipé pour améliorer la performance de vos applications NestJS.