Créer son premier projet NestJS

Créer son premier projet NestJS

Vous avez lu l'introduction, vous êtes convaincu. Maintenant, passons à la pratique : créer un projet NestJS de zéro, comprendre chaque fichier généré, et lancer votre première API en quelques minutes.

Prérequis : Node.js (version LTS, 20+), npm ou yarn. Vérifiez avec node -v et npm -v.

Installer la CLI NestJS

La CLI est l'outil de productivité central de NestJS. Elle génère les fichiers avec la bonne structure, gère les dépendances entre modules, et fournit des commandes de build/dev :

npm install -g @nestjs/cli

# Vérifier l'installation
nest --version

Si vous utilisez npx (pour ne pas installer globalement), remplacez nest par npx @nestjs/cli dans toutes les commandes qui suivent.

Créer le projet

nest new mon-api

La CLI vous pose deux questions :

  1. Gestionnaire de paquets : npm, yarn ou pnpm. Choisissez celui que vous utilisez habituellement.
  2. Strict mode TypeScript : répondez Yes — c'est un filet de sécurité gratuit.

L'installation prend environ une minute (téléchargement des dépendances). Une fois terminée, vous avez un projet prêt à tourner.

Structure des fichiers générés

Voici ce que vous obtenez :

mon-api/
├── src/
│   ├── main.ts              ← Point d'entrée
│   ├── app.module.ts        ← Module racine
│   ├── app.controller.ts    ← Controller par défaut
│   ├── app.controller.spec.ts ← Test du controller
│   └── app.service.ts       ← Service par défaut
├── test/
│   ├── app.e2e-spec.ts      ← Test end-to-end
│   └── jest-e2e.json        ← Config Jest E2E
├── node_modules/
├── .eslintrc.js             ← Linting
├── .prettierrc              ← Formatage
├── nest-cli.json            ← Configuration CLI
├── package.json             ← Dépendances et scripts
├── tsconfig.json            ← Configuration TypeScript
└── tsconfig.build.json      ← Config TS pour le build

Quatre fichiers méritent votre attention. Examinons-les un par un.

main.ts : le point d'entrée

C'est le fichier qui démarre tout. Son rôle : créer l'application NestJS et la lancer sur un port :

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
  console.log('🚀 Application running on http://localhost:3000');
}
bootstrap();

NestFactory.create() prend le module racine (AppModule) et construit l'application en résolvant toutes les dépendances. C'est ici que vous ajouterez plus tard :

  • La validation globale avec ValidationPipe
  • Le CORS avec app.enableCors()
  • Un préfixe API avec app.setGlobalPrefix('api')
  • La documentation Swagger

Voici à quoi ressemble un main.ts de production, avec ces éléments configurés :

import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Validation automatique des DTOs
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,           // Supprime les champs non déclarés
      forbidNonWhitelisted: true, // Erreur si champ inconnu
      transform: true,           // Convertit les types automatiquement
    }),
  );

  // Préfixe global : toutes les routes commencent par /api
  app.setGlobalPrefix('api');

  // CORS pour le frontend
  app.enableCors({
    origin: ['http://localhost:3000'],
    credentials: true,
  });

  // Documentation Swagger
  const config = new DocumentBuilder()
    .setTitle('Mon API')
    .setDescription('Documentation de l\'API')
    .setVersion('1.0')
    .addBearerAuth()
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api/docs', app, document);

  await app.listen(3002);
  console.log('🚀 API: http://localhost:3002');
  console.log('📚 Docs: http://localhost:3002/api/docs');
}
bootstrap();

app.module.ts : le module racine

Le module racine est la colonne vertébrale de l'application. Il déclare tous les modules, controllers et services :

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

@Module({
  imports: [],           // Modules importés
  controllers: [AppController], // Controllers de ce module
  providers: [AppService],      // Services disponibles
})
export class AppModule {}

Le décorateur @Module prend trois propriétés clés :

  • imports : les autres modules dont celui-ci dépend (ex: DatabaseModule, AuthModule)
  • controllers : les controllers qui gèrent les routes HTTP
  • providers : les services injectables (logique métier)

Au fur et à mesure que le projet grandit, vous ajouterez des modules dans imports. Une application de production peut facilement avoir 15 à 20 modules — et c'est justement ce qui la rend maintenable.

app.controller.ts : le premier endpoint

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

Trois choses à noter :

  1. @Controller() sans argument signifie que les routes sont à la racine (/). Avec @Controller('users'), elles seraient sous /users.
  2. @Get() mappe la méthode sur GET /. On peut aussi utiliser @Get('profile') pour GET /profile.
  3. Le service est injecté via le constructeur (private readonly appService). NestJS résout cette dépendance automatiquement.

app.service.ts : la logique métier

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

Le décorateur @Injectable() indique à NestJS que cette classe peut être injectée comme dépendance. C'est tout. Par défaut, les services sont des singletons — une seule instance partagée dans toute l'application.

Lancer le serveur de développement

# Démarrer avec rechargement automatique (hot reload)
npm run start:dev

Le serveur écoute sur http://localhost:3000. Testez-le :

curl http://localhost:3000
# → Hello World!

Le mode start:dev utilise --watch : chaque modification de fichier relance automatiquement la compilation et le serveur. Pas besoin de nodemon.

Les scripts npm disponibles

Le package.json généré contient plusieurs scripts utiles :

# Développement (avec hot reload)
npm run start:dev

# Production (compile puis exécute)
npm run build
npm run start:prod

# Tests unitaires
npm run test

# Tests avec surveillance (re-run automatique)
npm run test:watch

# Couverture de code
npm run test:cov

# Tests end-to-end
npm run test:e2e

# Linting
npm run lint

Générer votre premier module

Vous avez un projet qui tourne. Maintenant, ajoutons une fonctionnalité. La CLI génère un module complet avec une seule commande :

nest generate resource users

La CLI vous demande :

  1. Transport layer : choisissez REST API
  2. Generate CRUD entry points : répondez Yes

Résultat : NestJS crée 6 fichiers d'un coup :

src/users/
├── dto/
│   ├── create-user.dto.ts     ← DTO pour la création
│   └── update-user.dto.ts     ← DTO pour la mise à jour
├── entities/
│   └── user.entity.ts         ← Entité (modèle de données)
├── users.controller.ts        ← 5 routes CRUD pré-générées
├── users.module.ts            ← Module auto-importé dans AppModule
├── users.service.ts           ← Service avec méthodes CRUD
└── users.controller.spec.ts   ← Test unitaire

Et le module est automatiquement ajouté dans les imports de AppModule. Vous avez un CRUD fonctionnel (avec des méthodes placeholder) en 10 secondes.

nest-cli.json : la configuration

Ce fichier configure le comportement de la CLI :

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true
  }
}

deleteOutDir: true signifie que le dossier dist/ est nettoyé avant chaque build — pas de fichiers orphelins qui traînent.

Prochaines étapes

Vous avez un projet NestJS fonctionnel avec un endpoint Hello World et la capacité de générer des modules CRUD en une commande. Les prochains articles de cette série couvriront :

  • Modules, Controllers et Services en profondeur — comment les assembler proprement
  • L'injection de dépendances — le mécanisme qui rend tout ça possible
  • Connecter une base PostgreSQL avec TypeORM
  • Créer des entités et gérer les relations
  • Authentification JWT et contrôle d'accès par rôles

Pour approfondir en attendant : documentation officielle NestJS et documentation de la CLI.

Commentaires

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Les champs obligatoires sont indiqués avec *