Cours NestJS · Module 1: Bases de NestJS

Bienvenue dans l'Empire ! Introduction à NestJS

5 min de lecture
Dans cette leçon8

Salve, jeune légionnaire ! Bienvenue dans le système technologique romain ! Je suis le consul Caesar.js, et tu deviendras mon jeune légionnaire dans une campagne à travers les vastes provinces de NestJS - le framework Node.js le plus puissant de l'Empire d'Internet !

Tout comme l'Empire romain a besoin d'une construction solide, d'une légion expérimentée et de cohortes bien organisées, une application NestJS nécessite une architecture bien pensée, de la modularité et une approche professionnelle pour construire des applications côté serveur évolutives.

Qu'est-ce que NestJS ?

NestJS est un framework Node.js progressif pour construire des applications côté serveur efficaces et évolutives. Il a été construit en TypeScript et tire parti des meilleurs patrons de l'écosystème JavaScript, en les combinant avec de puissants décorateurs et un système d'injection de dépendances connu d'Angular.

Imagine que NestJS est notre puissant Empire romain :

1// Nos cohortes romaines (modules)
2import { Module } from '@nestjs/common';
3import { LegionModule } from './legion/legion.module';
4import { TributesModule } from './tributes/tributes.module';
5import { ForumModule } from './forum/forum.module';
6
7@Module({
8  imports: [LegionModule, TributesModule, ForumModule],
9})
10export class ImperiumModule {}

Fondamentaux de NestJS

1. Les décorateurs - nos insignes de rang romains

Dans NestJS, les décorateurs sont comme les insignes de rang romains - ils marquent le rang et la fonction de chaque élément de la légion :

1// Le consul contrôle toute l'opération
2@Controller('consul')
3export class ConsulController {
4  // Gère les ordres concernant les tributs
5  @Get('tributes')
6  findTributes() {
7    return 'Les tributs de toutes les provinces !';
8  }
9
10  // Recrute de nouveaux légionnaires
11  @Post('legion')
12  recruitLegionary(@Body() newLegionary: CreateLegionaryDto) {
13    return 'Nouveau légionnaire recruté !';
14  }
15}

2. Les modules - structures de notre Empire

Chaque module est une structure distincte de l'Empire avec un objectif précis :

1// Structure responsable de la légion
2@Module({
3  controllers: [LegionController],
4  providers: [LegionService],
5  exports: [LegionService],
6})
7export class LegionModule {}
8
9// Structure responsable des tributs
10@Module({
11  controllers: [TributesController],
12  providers: [TributesService],
13  imports: [DatabaseModule],
14})
15export class TributesModule {}

3. L'injection de dépendances - la chaîne de commandement

Le système de DI dans NestJS est comme la hiérarchie dans une légion romaine - chacun sait de qui recevoir des ordres :

1@Injectable()
2export class TributesService {
3  constructor(
4    private readonly mapService: MapService,
5    private readonly collectorService: CollectorService,
6  ) {}
7
8  async findTributes(province: string) {
9    const location = await this.mapService.getProvince(province);
10    return this.collectorService.collect(location);
11  }
12}

Installation et premiers pas

Pour commencer notre campagne dans l'Empire, il te faut l'équipement approprié. La base, c'est Node.js : le générateur de projets nest new de NestJS 12 exige la version 22.22.3 ou plus récente de la ligne 22, ou la 24.15 ou plus récente. Tu peux la vérifier avec la commande node -v. Ensuite, quatre commandes suffisent :

1# Installe le NestJS CLI - nos outils d'ingénierie
2npm install -g @nestjs/cli
3
4# Crée un nouvel Empire (projet)
5nest new roman-imperium
6
7# Entre dans le système
8cd roman-imperium
9
10# Lève les étendards de la légion (démarre le serveur)
11npm run start:dev

Structure de l'Empire romain (projet)

1roman-imperium/
2├── src/
3│   ├── main.ts               # Forum Romanum - point d'entrée
4│   ├── app.module.ts         # Le Sénat principal
5│   ├── app.controller.ts     # Consul - contrôleur principal
6│   ├── app.service.ts        # Primus Centurion
7│   ├── legion/               # Structure de la légion
8│   │   ├── legion.module.ts
9│   │   ├── legion.controller.ts
10│   │   └── legion.service.ts
11│   ├── tributes/             # Structure des tributs
12│   └── forum/                # Structure du forum
13├── test/                     # Terrain d'entraînement
14├── package.json              # Registre impérial
15└── nest-cli.json             # Instructions d'ingénierie

Premier endpoint - signal impérial

Créons notre premier endpoint qui signalera la présence de l'Empire :

1// app.controller.ts
2import { Controller, Get } from '@nestjs/common';
3import { AppService } from './app.service';
4
5@Controller()
6export class AppController {
7  constructor(private readonly appService: AppService) {}
8
9  @Get()
10  getSignal(): string {
11    return 'Imperium prêt pour la campagne !';
12  }
13
14  @Get('status')
15  getImperiumStatus() {
16    return {
17      legion: 'Prête pour la campagne',
18      tributes: 'Collecte en cours...',
19      destination: 'Nouvelles provinces',
20      consul: 'Caesar.js à votre service !',
21    };
22  }
23}

Avantages de NestJS pour l'Empire

  1. Modularité - chaque structure de l'Empire a sa propre tâche
  2. TypeScript - une précision digne de l'ingénierie romaine
  3. Décorateurs - des marquages lisibles comme les signaux de la légion
  4. Tests - nous vérifions tout avant de partir en campagne
  5. Documentation - des cartes automatiques de nos provinces (Swagger)
  6. Évolutivité - d'un petit castrum jusqu'à l'architecture de l'Empire

Exercice pratique

L'heure du premier test impérial ! Crée un contrôleur simple qui gérera des informations de base sur la légion :

1// legion.controller.ts
2import { Controller, Get, Post, Body } from '@nestjs/common';
3
4@Controller('legion')
5export class LegionController {
6  private legionaries = [
7    { name: 'Marcus Aurelius', role: 'Centurion', experience: 10 },
8    { name: 'Julius Brutus', role: 'Engineer', experience: 5 },
9  ];
10
11  @Get()
12  getAllLegionaries() {
13    return this.legionaries;
14  }
15
16  @Post()
17  addLegionary(@Body() newLegionary: any) {
18    this.legionaries.push(newLegionary);
19    return { message: 'Un nouveau légionnaire a rejoint la légion !', legionary: newLegionary };
20  }
21}

Campagnes à venir

Dans les modules suivants, nous découvrirons :

  • Services et Providers - les rôles de spécialistes dans l'Empire
  • Middleware - les systèmes de défense
  • Guards - les gardes prétoriens
  • Interceptors - l'interception des signaux
  • Pipes - le traitement des données
  • Intégration de base de données - les entrepôts de tributs
  • Authentification - l'identification des citoyens de l'Empire
  • WebSockets - la communication entre provinces
  • Tests - les préparatifs avant une campagne

Prépare-toi, jeune légionnaire ! Une campagne passionnante à travers les provinces du développement web moderne nous attend. NestJS n'est pas qu'un framework - c'est un outil puissant pour construire des applications de classe entreprise qui t'aidera à collecter les tributs les plus précieux du monde de la programmation !

Souviens-toi : un vrai légionnaire-programmeur ne part jamais sans une carte (documentation) et une boussole (bonnes pratiques) !

Code de cette leçon : src/app.controller.ts
1// NestJS Imperium Romanum - Premier projet
2import { Controller, Get, Post, Body } from '@nestjs/common';
3import { AppService } from './app.service';
4
5console.log("Salve ! Bienvenue dans l'Imperium Romanum NestJS !");
6console.log("Apprends les bases d'un framework pour les vrais bâtisseurs d'empire\n");
7
8// ===========================================
9// 1. Contrôleur de base - Empereur du système
10// ===========================================
11console.log("=== 1. CONTRÔLEUR DE BASE ===");
12
13@Controller()
14export class AppController {
15  constructor(private readonly appService: AppService) {}
16
17  @Get()
18  getWelcome(): string {
19    return this.appService.getWelcome();
20  }
21
22  @Get('status')
23  getImperiumStatus() {
24    return {
25      imperium: 'Roma Aeterna NestJS',
26      caesar: 'Augustus.js',
27      legiones: 'Ready for coding!',
28      status: 'We build roads through TypeScript',
29      framework: 'NestJS',
30      timestamp: new Date().toISOString(),
31    };
32  }
33}
34
35// ===========================================
36// 2. Contrôleur de légion - Gestion des soldats
37// ===========================================
38console.log("=== 2. CONTRÔLEUR DE LÉGION ===");
39
40interface Legionary {
41  id: number;
42  name: string;
43  rank: string;
44  experience: number;
45  legio: string;
46}
47
48@Controller('legiones')
49export class LegionController {
50  private legionaries: Legionary[] = [
51    {
52      id: 1,
53      name: 'Marcus Aurelius',
54      rank: 'Legatus',
55      experience: 10,
56      legio: 'Legio X Gemina'
57    },
58    {
59      id: 2,
60      name: 'Gaius Julius',
61      rank: 'Centurio',
62      experience: 8,
63      legio: 'Legio III Augusta'
64    },
65    {
66      id: 3,
67      name: 'Lucius Scipio',
68      rank: 'Optio',
69      experience: 6,
70      legio: 'Legio IX Hispana'
71    },
72    {
73      id: 4,
74      name: 'Titus Flavius',
75      rank: 'Miles',
76      experience: 5,
77      legio: 'Legio XII Fulminata'
78    }
79  ];
80
81  @Get()
82  getAllLegionaries(): Legionary[] {
83    console.log("Liste de tous les légionnaires");
84    return this.legionaries;
85  }
86
87  @Get('commanders')
88  getCommanders(): Legionary[] {
89    console.log("Liste des commandants");
90    return this.legionaries.filter(l => l.rank === 'Legatus' || l.rank === 'Centurio');
91  }
92
93  @Post()
94  recruitLegionary(@Body() newLegionary: Omit<Legionary, 'id'>): Legionary {
95    const legionary: Legionary = {
96      id: this.getNextId(),
97      ...newLegionary,
98    };
99
100    this.legionaries.push(legionary);
101    console.log(`Nouveau légionnaire : ${legionary.name} en tant que ${legionary.rank}`);
102
103    return legionary;
104  }
105
106  @Get('stats')
107  getLegionStats() {
108    const totalExperience = this.legionaries.reduce((sum, l) => sum + l.experience, 0);
109    const avgExperience = totalExperience / this.legionaries.length;
110
111    const rankCounts = this.legionaries.reduce((counts, l) => {
112      counts[l.rank] = (counts[l.rank] || 0) + 1;
113      return counts;
114    }, {} as Record<string, number>);
115
116    return {
117      totalMilites: this.legionaries.length,
118      totalExperience,
119      averageExperience: Math.round(avgExperience * 10) / 10,
120      rankDistribution: rankCounts,
121      mostExperienced: this.legionaries.reduce((prev, current) =>
122        prev.experience > current.experience ? prev : current
123      ),
124    };
125  }
126
127  private getNextId(): number {
128    return Math.max(...this.legionaries.map(l => l.id), 0) + 1;
129  }
130}
131
132// ===========================================
133// 3. Contrôleur de tributum - Gestion des impôts
134// ===========================================
135console.log("=== 3. CONTRÔLEUR DE TRIBUTUM ===");
136
137interface Tributum {
138  id: number;
139  name: string;
140  value: number;
141  provincia: string;
142  collected: boolean;
143  collectedBy?: string;
144}
145
146@Controller('tributum')
147export class TributumController {
148  private tributes: Tributum[] = [
149    {
150      id: 1,
151      name: 'Aurum Galliae',
152      value: 50000,
153      provincia: 'Gallia',
154      collected: true,
155      collectedBy: 'Marcus Aurelius'
156    },
157    {
158      id: 2,
159      name: 'Argentum Hispaniae',
160      value: 75000,
161      provincia: 'Hispania',
162      collected: false
163    },
164    {
165      id: 3,
166      name: 'Aes Britanniae',
167      value: 100000,
168      provincia: 'Britannia',
169      collected: true,
170      collectedBy: 'Gaius Julius'
171    },
172    {
173      id: 4,
174      name: 'Triticum Aegypti',
175      value: 25000,
176      provincia: 'Aegyptus',
177      collected: false
178    }
179  ];
180
181  @Get()
182  getAllTributes(): Tributum[] {
183    console.log("Liste de tous les impôts");
184    return this.tributes;
185  }
186
187  @Get('collected')
188  getCollectedTributes(): Tributum[] {
189    console.log("Impôts collectés");
190    return this.tributes.filter(t => t.collected);
191  }
192
193  @Get('pending')
194  getPendingTributes(): Tributum[] {
195    console.log("Impôts en attente");
196    return this.tributes.filter(t => !t.collected);
197  }
198
199  @Post('collect/:id')
200  collectTribute(@Body() collector: { collectedBy: string }) {
201    const tributeId = 2;
202    const tribute = this.tributes.find(t => t.id === tributeId);
203
204    if (!tribute) {
205      return { success: false, message: 'Tributum non inventum!' };
206    }
207
208    if (tribute.collected) {
209      return { success: false, message: 'Tributum iam collectum!' };
210    }
211
212    tribute.collected = true;
213    tribute.collectedBy = collector.collectedBy;
214
215    console.log(`Impôt collecté : ${tribute.name} par ${collector.collectedBy}`);
216
217    return {
218      success: true,
219      message: `Félicitations ! ${collector.collectedBy} a collecté ${tribute.name} !`,
220      tribute,
221      reward: tribute.value
222    };
223  }
224
225  @Get('aerarium')
226  getAerarium() {
227    const collectedValue = this.tributes
228      .filter(t => t.collected)
229      .reduce((sum, t) => sum + t.value, 0);
230
231    const pendingValue = this.tributes
232      .filter(t => !t.collected)
233      .reduce((sum, t) => sum + t.value, 0);
234
235    return {
236      aerariumValue: collectedValue,
237      pendingValue,
238      totalValue: collectedValue + pendingValue,
239      collectionPercentage: Math.round((collectedValue / (collectedValue + pendingValue)) * 100)
240    };
241  }
242}
243
244// ===========================================
245// 4. Démonstration des appels d'API
246// ===========================================
247console.log("=== 4. DÉMONSTRATION DE L'API ===");
248
249class APIDemo {
250  static async demonstrateAPICalls() {
251    console.log("Démonstration des appels d'API :");
252
253    console.log("GET /legiones - Récupère les légionnaires :");
254    const legionaries = new LegionController().getAllLegionaries();
255    console.log(`${legionaries.length} légionnaires trouvés`);
256
257    console.log("\nGET /legiones/stats - Statistiques de la légion :");
258    const stats = new LegionController().getLegionStats();
259    console.log(`Total des milites : ${stats.totalMilites}, Expérience moy. : ${stats.averageExperience}`);
260
261    console.log("\nGET /tributum - Récupère les impôts :");
262    const tributes = new TributumController().getAllTributes();
263    console.log(`${tributes.length} impôts trouvés`);
264
265    console.log("\nGET /tributum/aerarium - État du trésor :");
266    const aerarium = new TributumController().getAerarium();
267    console.log(`Valeur de l'aerarium : ${aerarium.aerariumValue} deniers (${aerarium.collectionPercentage}% collecté)`);
268
269    return {
270      legionaries: legionaries.length,
271      tributes: tributes.length,
272      aerariumValue: aerarium.aerariumValue
273    };
274  }
275}
276
277// ===========================================
278// 5. Démarrage de la démonstration
279// ===========================================
280console.log("=== 5. DÉMARRAGE DE L'APPLICATION ===");
281
282async function runImperiumDemo() {
283  try {
284    console.log("Démarrage de la démo Imperium Romanum NestJS...");
285
286    const results = await APIDemo.demonstrateAPICalls();
287
288    console.log("\nRÉSUMÉ DE L'IMPERIUM :");
289    console.log(`Légionnaires : ${results.legionaries} milites`);
290    console.log(`Impôts : ${results.tributes} tributum`);
291    console.log(`Aerarium : ${results.aerariumValue} deniers`);
292    console.log("\nApplication NestJS prête pour la conquête !");
293
294    return results;
295  } catch (error) {
296    console.error("Erreur dans l'Imperium :", error);
297    throw error;
298  }
299}
300
301runImperiumDemo().then(() => {
302  console.log("\nAve Caesar ! Démo terminée avec succès !");
303  console.log("Félicitations - tu maîtrises les bases de NestJS !");
304});
305
306export { AppController, LegionController, TributumController };

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Qu'est-ce que NestJS ?

  2. 2. Avec quel langage de programmation NestJS est-il construit ?

Tâches pratiques dans le jeu

  • Éditeur de code

    Écris une classe StatusController avec le décorateur @Controller('status') et une méthode getStatus() avec le décorateur @Get(). La méthode doit renvoyer un objet avec un champ status, par exemple { status: 'Empire prêt pour la campagne' }.

  • Clique dans l'ordre

    Remets dans le bon ordre les étapes de création d'un nouveau projet NestJS :