Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🟢
Intermediaire 30 min NestJS

NestJS avec MongoDB : guide pratique

Pourquoi NestJS avec MongoDB : guide pratique ?

NestJS est un framework JavaScript/TypeScript basé sur les principes de la conception orientée objet et conçu pour aider les développeurs à créer des applications robustes, scalables et maintenables. L'intégration de MongoDB, une base de données NoSQL polyvalente, offre une solution efficace pour stocker et gérer les données complexes. Dans ce guide pratique, nous explorerons comment utiliser NestJS avec MongoDB pour construire une application complète.

Contexte réel : Un développeur backend peut avoir besoin d'une application qui nécessite un stockage de données dynamique, évoluant constamment et capable de traiter des volumes importants de données. MongoDB est idéal pour ce type de cas, offrant une performance élevée et une flexibilité dans la façon dont les données sont structurées.

Un cas d'usage concret en 2-3 phrases : Imaginez que vous travaillez sur un service e-commerce. Vous avez besoin d'une base de données capable de stocker des produits, des utilisateurs, des commandes et leurs détails. MongoDB offre une solution flexible qui peut s'échaler facilement avec la croissance de votre base d'utilisateurs.

Prerequis

Pour suivre ce tutoriel, vous aurez besoin des éléments suivants :

  • Node.js : >= 14.x
  • npm ou yarn : >= 6.x
  • MongoDB : >= 4.0 (pour l'environnement local)
  • Docker : Facultatif, pour exécuter MongoDB en conteneur

Concepts fondamentaux

1. Installation de NestJS et MongoDB

Tout d'abord, nous devons installer NestJS et les dépendances nécessaires.

npm install -g @nestjs/cli
nest new task-manager-api
cd task-manager-api

Ajoutez MongoDB en tant que dépendance :

npm install mongoose

2. Configuration de la connexion à MongoDB

Créez un fichier config/mongoose.config.ts pour configurer la connexion à MongoDB.

// src/config/mongoose.config.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRootAsync({
      useFactory: async () => ({
        uri: 'mongodb://localhost:27017/task-manager', // Change this URI for production
        useNewUrlParser: true,
        useUnifiedTopology: true,
      }),
    }),
  ],
})
export class MongooseConfigModule {}

3. Création d'un modèle Mongoose

Créez un modèle pour les tâches.

// src/tasks/task.schema.ts
import { Schema, Document } from 'mongoose';

export const TaskSchema = new Schema({
  title: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    default: '',
  },
  completed: {
    type: Boolean,
    default: false,
  },
});

export interface Task extends Document {
  readonly id: string;
  title: string;
  description?: string;
  completed: boolean;
}

4. Création d'un service pour les tâches

// src/tasks/task.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Task, TaskSchema } from './task.schema';

@Injectable()
export class TasksService {
  constructor(@InjectModel(Task.name) private readonly taskModel: Model<Task>) {}

  async create(taskDto: Partial<Task>): Promise<Task> {
    const createdTask = new this.taskModel(taskDto);
    return createdTask.save();
  }

  async findAll(): Promise<Task[]> {
    return this.taskModel.find().exec();
  }

  async findOne(id: string): Promise<Task> {
    return this.taskModel.findById(id).exec();
  }

  async update(id: string, taskDto: Partial<Task>): Promise<Task> {
    return this.taskModel.findByIdAndUpdate(id, taskDto, { new: true }).exec();
  }

  async remove(id: string): Promise<void> {
    await this.taskModel.deleteOne({ _id: id }).exec();
  }
}

5. Création d'un controller pour les tâches

// src/tasks/task.controller.ts
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { TasksService } from './task.service';
import { Task } from './task.schema';

@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Post()
  async create(@Body() taskDto: Partial<Task>): Promise<Task> {
    return this.tasksService.create(taskDto);
  }

  @Get()
  async findAll(): Promise<Task[]> {
    return this.tasksService.findAll();
  }

  @Get(':id')
  async findOne(@Param('id') id: string): Promise<Task> {
    return this.tasksService.findOne(id);
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() taskDto: Partial<Task>): Promise<Task> {
    return this.tasksService.update(id, taskDto);
  }

  @Delete(':id')
  async remove(@Param('id') id: string): Promise<void> {
    return this.tasksService.remove(id);
  }
}

6. Importation des modules

// src/app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { TasksController } from './tasks/task.controller';
import { TasksService } from './tasks/task.service';
import { TaskSchema, Task } from './tasks/task.schema';
import { MongooseConfigModule } from './config/mongoose.config';

@Module({
  imports: [
    MongooseConfigModule,
    MongooseModule.forFeature([{ name: Task.name, schema: TaskSchema }]),
  ],
  controllers: [TasksController],
  providers: [TasksService],
})
export class AppModule {}

Mise en pratique : projet fil rouge

Étape 1 : Initialisation du projet

nest new task-manager-api
cd task-manager-api
npm install mongoose

Étape 2 : Configuration de la connexion à MongoDB

Créez src/config/mongoose.config.ts :

// src/config/mongoose.config.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRootAsync({
      useFactory: async () => ({
        uri: 'mongodb://localhost:27017/task-manager',
        useNewUrlParser: true,
        useUnifiedTopology: true,
      }),
    }),
  ],
})
export class MongooseConfigModule {}

Étape 3 : Création d'un modèle Mongoose

Créez src/tasks/task.schema.ts :

// src/tasks/task.schema.ts
import { Schema, Document } from 'mongoose';

export const TaskSchema = new Schema({
  title: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    default: '',
  },
  completed: {
    type: Boolean,
    default: false,
  },
});

export interface Task extends Document {
  readonly id: string;
  title: string;
  description?: string;
  completed: boolean;
}

Étape 4 : Création d'un service pour les tâches

Créez src/tasks/task.service.ts :

// src/tasks/task.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Task, TaskSchema } from './task.schema';

@Injectable()
export class TasksService {
  constructor(@InjectModel(Task.name) private readonly taskModel: Model<Task>) {}

  async create(taskDto: Partial<Task>): Promise<Task> {
    const createdTask = new this.taskModel(taskDto);
    return createdTask.save();
  }

  async findAll(): Promise<Task[]> {
    return this.taskModel.find().exec();
  }

  async findOne(id: string): Promise<Task> {
    return this.taskModel.findById(id).exec();
  }

  async update(id: string, taskDto: Partial<Task>): Promise<Task> {
    return this.taskModel.findByIdAndUpdate(id, taskDto, { new: true }).exec();
  }

  async remove(id: string): Promise<void> {
    await this.taskModel.deleteOne({ _id: id }).exec();
  }
}

Étape 5 : Création d'un controller pour les tâches

Créez src/tasks/task.controller.ts :

// src/tasks/task.controller.ts
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { TasksService } from './task.service';
import { Task } from './task.schema';

@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Post()
  async create(@Body() taskDto: Partial<Task>): Promise<Task> {
    return this.tasksService.create(taskDto);
  }

  @Get()
  async findAll(): Promise<Task[]> {
    return this.tasksService.findAll();
  }

  @Get(':id')
  async findOne(@Param('id') id: string): Promise<Task> {
    return this.tasksService.findOne(id);
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() taskDto: Partial<Task>): Promise<Task> {
    return this.tasksService.update(id, taskDto);
  }

  @Delete(':id')
  async remove(@Param('id') id: string): Promise<void> {
    return this.tasksService.remove(id);
  }
}

Étape 6 : Importation des modules

Créez src/app.module.ts :

// src/app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { TasksController } from './tasks/task.controller';
import { TasksService } from './tasks/task.service';
import { TaskSchema, Task } from './tasks/task.schema';
import { MongooseConfigModule } from './config/mongoose.config';

@Module({
  imports: [
    MongooseConfigModule,
    MongooseModule.forFeature([{ name: Task.name, schema: TaskSchema }]),
  ],
  controllers: [TasksController],
  providers: [TasksService],
})
export class AppModule {}

Étape 7 : Lancement de l'application

npm run start

Accédez à http://localhost:3000/tasks pour voir les routes disponibles.

Erreurs fréquentes et debugging

1. Erreur : Connection to MongoDB failed

Code incorrect :

// src/config/mongoose.config.ts
MongooseModule.forRootAsync({
  useFactory: async () => ({
    uri: 'mongodb://localhost:27017/task-manager',
    useNewUrlParser: true,
    useUnifiedTopology: true,
  }),
}),

Code correct :

// src/config/mongoose.config.ts
MongooseModule.forRoot('mongodb://localhost:27017/task-manager', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
}),

2. Erreur : Cannot read property 'save' of undefined

Code incorrect :

// src/tasks/task.service.ts
async create(taskDto: Partial<Task>): Promise<Task> {
  const createdTask = new this.taskModel(taskDto);
  return await createdTask.save();
}

Code correct :

// src/tasks/task.service.ts
async create(taskDto: Partial<Task>): Promise<Task> {
  const createdTask = new this.taskModel(taskDto);
  return createdTask.save();
}

3. Erreur : Validation failed

Code incorrect :

// src/tasks/task.schema.ts
title: {
  type: String,
  required: true,
},
description: {
  type: String,
  default: '',
},
completed: {
  type: Boolean,
  default: false,
},

Code correct :

// src/tasks/task.schema.ts
title: {
  type: String,
  required: true,
  minlength: 1,
},
description: {
  type: String,
  maxlength: 255,
},
completed: {
  type: Boolean,
  default: false,
},

Pour aller plus loin

1. Authentification et sécurité

Intégrez une authentification JWT pour sécuriser vos routes.

2. Migrations de base de données

Utilisez des outils comme mongoose-migrate pour gérer les migrations de base de données.

3. Tests unitaires et d'intégration

Écrivez des tests unitaires et d'intégration pour vos services et contrôleurs.

Défi pratique : Ajout de l'authentification JWT

Ajoutez une fonctionnalité d'authentification JWT à votre application pour sécuriser les routes sensibles.

  1. Installez @nestjs/jwt et jsonwebtoken.
npm install @nestjs/jwt jsonwebtoken
  1. Créez un module pour l'authentification.
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';

@Module({
  imports: [
    JwtModule.registerAsync({
      useFactory: async () => ({
        secret: 'your-secret-key',
        signOptions: { expiresIn: '60s' },
      }),
    }),
  ],
})
export class AuthModule {}
  1. Créez un service pour gérer les tokens JWT.
// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  async generateToken(userId: string): Promise<string> {
    return this.jwtService.signAsync({ userId });
  }
}
  1. Ajoutez un middleware pour la vérification des tokens JWT.
// src/auth/jwt.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class JwtMiddleware implements NestMiddleware {
  constructor(private readonly jwtService: JwtService) {}

  async use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers['authorization']?.split(' ')[1];

    if (token) {
      try {
        const decoded = this.jwtService.verify(token);
        req.user = decoded;
      } catch {}
    }

    next();
  }
}
  1. Ajoutez le middleware à votre application.
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { JwtMiddleware } from './auth/jwt.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(JwtMiddleware);
  await app.listen(3000);
}
bootstrap();
  1. Protégez une route.
// src/tasks/task.controller.ts
@UseGuards(AuthGuard('jwt'))
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Post()
  async create(@Body() taskDto: Partial<Task>, @Request() req): Promise<Task> {
    return this.tasksService.create(taskDto);
  }

  // ... autres routes
}

Lien : https://docs.nestjs.com/security/authentication

Conclusion

Ce guide pratique vous a montré comment créer une application NestJS avec une base de données MongoDB. Vous avez appris à configurer la connexion à la base de données, à créer des modèles Mongoose, et à manipuler les données via des services et contrôleurs. Vous avez également exploré des concepts avancés comme l'authentification JWT et la sécurité des routes.

N'hésitez pas à explorer les ressources supplémentaires pour approfondir votre compréhension de NestJS et d'autres frameworks Node.js.

Besoin d'aide sur NestJS ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Quel est le but du tutoriel 'NestJS avec MongoDB : guide pratique'?
Ce tutoriel vise à guider les développeurs sur la façon d'intégrer MongoDB dans une application NestJS, en couvrant les aspects de base tels que la configuration, la création de modèles et le développement de contrôleurs.
Quelles sont les principales technologies utilisées dans ce tutoriel?
Ce tutoriel utilise principalement NestJS comme framework pour l'application côté serveur et MongoDB comme base de données. Il peut également inclure d'autres technologies liées à ces deux plateformes.
Quelle est la structure générale des fichiers que nous allons créer dans ce tutoriel?
Dans ce tutoriel, nous allons créer une application NestJS avec une structure de projet typique, y compris des fichiers pour les contrôleurs (pour gérer les requêtes HTTP), les services (pour la logique métier) et les modèles (pour définir l'interface de données MongoDB).

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.