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

NestJS avec PostgreSQL : guide pratique

Voici une version complète en français d'un tutoriel approfondi sur NestJS avec PostgreSQL :

Pourquoi NestJS avec PostgreSQL : guide pratique ?

Contexte réel : pourquoi un dev a besoin de ça au quotidien

Un développeur backend doit souvent intégrer des bases de données pour stocker et gérer les données de son application. PostgreSQL est l'un des systèmes de gestion de base de données (SGBD) les plus populaires et performants disponibles aujourd'hui. NestJS, un cadre d'architecture basé sur le principe du microservice, offre une structure solide et flexible pour développer des applications robustes.

Un cas d'utilisation concret en 2-3 phrases

Imaginons que nous voulions créer une application de gestion de tâches. Nous aurions besoin d'une API RESTful pour ajouter, mettre à jour, supprimer et récupérer les tâches. En utilisant NestJS avec PostgreSQL, nous pouvons facilement construire cette application en maintenant une architecture modulaire et scalable.

Prerequis

  • Connaissances nécessaires :

    • JavaScript/TypeScript
    • Connaissance de la programmation asynchrone
    • Familiarité avec les SGBD (PostgreSQL)
    • Connaissance des concepts d'API RESTful
  • Outils à installer :

    • Node.js et npm (>= v12.0.0)
    • PostgreSQL (>= 10)
    • Yarn (optionnel, mais recommandé)

Concepts fondamentaux

1. Installation et configuration de NestJS

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

Structure du projet :

  • src/app.controller.ts : Point d'entrée de l'application.
  • src/app.service.ts : Service principal de l'application.
  • src/main.ts : Point d'entrée principal.

2. Installation et configuration de PostgreSQL

## Installez PostgreSQL
sudo apt-get update && sudo apt-get install postgresql postgresql-contrib

## Créez un utilisateur et une base de données
sudo -u postgres createuser --interactive
## Choisissez le rôle "createrole" lors de la création
sudo -u postgres createdb task_management_db

Structure de la base de données :

CREATE TABLE tasks (
  id SERIAL PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  description TEXT,
  completed BOOLEAN DEFAULT FALSE
);

3. Installation et configuration des dépendances

npm install @nestjs/typeorm typeorm pg @nestjs/common @nestjs/core class-validator class-transformer

Configuration de TypeORM :

Créez un fichier ormconfig.json à la racine du projet :

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "task_management_user",
  "password": "your_password",
  "database": "task_management_db",
  "synchronize": true,
  "logging": false,
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "migrations": ["dist/migrations/*{.ts,.js}"],
  "subscribers": []
}

4. Création d'une entité Task

// src/tasks/task.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column({ nullable: true })
  description?: string;

  @Column({ default: false })
  completed: boolean;
}

5. Création d'un service Task

// src/tasks/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 TasksService {
  constructor(
    @InjectRepository(Task)
    private readonly tasksRepository: Repository<Task>,
  ) {}

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

  findOne(id: number): Promise<Task> {
    return this.tasksRepository.findOne(id);
  }

  create(task: Task): Promise<Task> {
    return this.tasksRepository.save(task);
  }

  update(id: number, task: Task): Promise<void> {
    task.id = id;
    return this.tasksRepository.save(task);
  }

  delete(id: number): Promise<void> {
    await this.tasksRepository.delete(id);
  }
}

6. Création d'un contrôleur Task

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

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

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

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

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

  @Put(':id')
  update(@Param('id') id: string, @Body() task: Task): Promise<void> {
    return this.tasksService.update(+id, task);
  }

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

Mise en pratique : projet fil rouge

Étape 1 : Initialisation du projet

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

Étape 2 : Configuration de PostgreSQL

Suivez les étapes précédentes pour installer et configurer PostgreSQL.

Étape 3 : Installation des dépendances

npm install @nestjs/typeorm typeorm pg @nestjs/common @nestjs/core class-validator class-transformer

Créez un fichier ormconfig.json à la racine du projet :

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "task_management_user",
  "password": "your_password",
  "database": "task_management_db",
  "synchronize": true,
  "logging": false,
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "migrations": ["dist/migrations/*{.ts,.js}"],
  "subscribers": []
}

Étape 4 : Création d'une entité Task

// src/tasks/task.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column({ nullable: true })
  description?: string;

  @Column({ default: false })
  completed: boolean;
}

Étape 5 : Création d'un service Task

// src/tasks/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 TasksService {
  constructor(
    @InjectRepository(Task)
    private readonly tasksRepository: Repository<Task>,
  ) {}

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

  findOne(id: number): Promise<Task> {
    return this.tasksRepository.findOne(id);
  }

  create(task: Task): Promise<Task> {
    return this.tasksRepository.save(task);
  }

  update(id: number, task: Task): Promise<void> {
    task.id = id;
    return this.tasksRepository.save(task);
  }

  delete(id: number): Promise<void> {
    await this.tasksRepository.delete(id);
  }
}

Étape 6 : Création d'un contrôleur Task

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

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

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

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

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

  @Put(':id')
  update(@Param('id') id: string, @Body() task: Task): Promise<void> {
    return this.tasksService.update(+id, task);
  }

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

Étape 7 : Configuration du module principal

// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TasksController } from './tasks/task.controller';
import { TasksService } from './tasks/task.service';
import { Task } from './tasks/task.entity';

@Module({
  imports: [TypeOrmModule.forRoot(), TypeOrmModule.forFeature([Task])],
  controllers: [TasksController],
  providers: [TasksService],
})
export class AppModule {}

Étape 8 : Exécution de l'application

npm run start

Erreurs frequentes et debugging

Erreur 1 : "QueryFailedError: relation 'tasks' does not exist"

Code incorrect :

// src/tasks/task.repository.ts
import { Repository } from 'typeorm';
import { Task } from './task.entity';

export class TasksRepository extends Repository<Task> {}

Code correct :

// src/tasks/task.repository.ts
import { EntityRepository, Repository } from 'typeorm';
import { Task } from './task.entity';

@EntityRepository(Task)
export class TasksRepository extends Repository<Task> {}

Erreur 2 : "Cannot find module 'pg'"

Code incorrect :

npm install pg

Code correct :

npm install pg --save

Erreur 3 : "TypeError: Cannot read property 'length' of undefined"

Code incorrect :

// src/tasks/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 TasksService {
  constructor(
    @InjectRepository(Task)
    private readonly tasksRepository: Repository<Task>,
  ) {}

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

Code correct :

// src/tasks/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 TasksService {
  constructor(
    @InjectRepository(Task)
    private readonly tasksRepository: Repository<Task>,
  ) {}

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

  findOne(id: number): Promise<Task> {
    return this.tasksRepository.findOne(id);
  }

  create(task: Task): Promise<Task> {
    return this.tasksRepository.save(task);
  }

  update(id: number, task: Task): Promise<void> {
    task.id = id;
    return this.tasksRepository.save(task);
  }

  delete(id: number): Promise<void> {
    await this.tasksRepository.delete(id);
  }
}

Pour aller plus loin

1. Ajouter des validations avec class-validator et class-transformer

Ajoutez des annotations de validation à vos entités pour vous assurer que les données sont correctement formatées.

// src/tasks/task.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { IsNotEmpty, IsString, IsOptional, IsBoolean } from 'class-validator';

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  id: number;

  @IsNotEmpty()
  @IsString()
  title: string;

  @IsOptional()
  @IsString()
  description?: string;

  @IsBoolean()
  completed: boolean;
}

2. Utiliser les intercepteurs pour ajouter des fonctionnalités communes

Créez un intercepteur pour ajouter automatiquement une date de création et de modification à chaque entité.

// src/tasks/created-at.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class CreatedAtInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = new Date();
    return next.handle().pipe(
      tap((data) => {
        if (Array.isArray(data)) {
          data.forEach((item) => (item.createdAt = now));
        } else {
          data.createdAt = now;
        }
      }),
    );
  }
}

Appliquez l'intercepteur à votre service :

// src/tasks/task.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Task } from './task.entity';
import { CreatedAtInterceptor } from '../shared/created-at.interceptor';

@Injectable()
export class TasksService {
  constructor(
    @InjectRepository(Task)
    private readonly tasksRepository: Repository<Task>,
  ) {}

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

  findOne(id: number): Promise<Task> {
    return this.tasksRepository.findOne(id);
  }

  create(@CreatedAtInterceptor() task: Task): Promise<Task> {
    return this.tasksRepository.save(task);
  }

  update(id: number, task: Task): Promise<void> {
    task.id = id;
    return this.tasksRepository.save(task);
  }

  delete(id: number): Promise<void> {
    await this.tasksRepository.delete(id);
  }
}

3. Créer un module pour les tâches

Organisez votre code en modules pour une meilleure structure et maintenabilité.

// src/tasks/task.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TasksController } from './task.controller';
import { TasksService } from './task.service';
import { Task } from './task.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Task])],
  controllers: [TasksController],
  providers: [TasksService],
})
export class TasksModule {}

Ajoutez le module à l'application principale :

// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TasksModule } from './tasks/task.module';

@Module({
  imports: [TypeOrmModule.forRoot(), TasksModule],
})
export class AppModule {}

Défi pratique : créer un CLI tool pour gérer les tâches

Créez un CLI tool qui permet de créer, lire, mettre à jour et supprimer des tâches en utilisant NestJS.

nest generate cli task-cli

Ajoutez une commande pour ajouter une tâche :

// src/tasks/cli/task-command.ts
import { Command, Option, injectable } from '@nestjs/cli';
import { TasksService } from '../task.service';

@injectable()
export default class TaskCommand implements Command {
  readonly command = 'add';
  readonly description = 'Add a new task';

  constructor(private readonly tasksService: TasksService) {}

  async run(options: any): Promise<any> {
    const { title, description } = options;
    await this.tasksService.create({ title, description });
    console.log('Task added successfully');
  }

  getOptions(): Option[] {
    return [
      {
        flags: '-t, --title <title>',
        required: true,
        description: 'The title of the task',
      },
      {
        flags: '-d, --description [description]',
        required: false,
        description: 'The description of the task',
      },
    ];
  }
}

Ajoutez une commande pour lire toutes les tâches :

// src/tasks/cli/task-command.ts
import { Command, Option, injectable } from '@nestjs/cli';
import { TasksService } from '../task.service';

@injectable()
export default class TaskCommand implements Command {
  readonly command = 'list';
  readonly description = 'List all tasks';

  constructor(private readonly tasksService: TasksService) {}

  async run(options: any): Promise<any> {
    const tasks = await this.tasksService.findAll();
    console.log(tasks);
  }

  getOptions(): Option[] {
    return [];
  }
}

Ajoutez les commandes au module CLI :

// src/tasks/cli/task.module.ts
import { Module } from '@nestjs/common';
import { TasksService } from '../task.service';

@Module({
  providers: [TasksService],
})
export class TaskModule {}

Ajoutez le module CLI à l'application principale :

// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TasksModule } from './tasks/task.module';
import { TaskCliModule } from './tasks/cli/task.module';

@Module({
  imports: [TypeOrmModule.forRoot(), TasksModule, TaskCliModule],
})
export class AppModule {}

Enfin, exécutez les commandes :

nest task add -t "Buy groceries" -d "Remember to buy milk and bread"
nest task list

Cela devrait vous donner une bonne base pour créer votre propre CLI tool avec NestJS. Vous pouvez ajouter davantage de fonctionnalités en suivant la même structure.

Besoin d'aide sur NestJS ?

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

Recevoir des conseils

Questions frequentes

Comment installer NestJS avec PostgreSQL?
Pour installer NestJS avec PostgreSQL, commencez par créer un nouveau projet NestJS en utilisant la commande 'nest new project-name'. Ensuite, installez le package 'typeorm' et 'pg' via npm avec la commande 'npm install typeorm pg'.
Comment configurer l'ORM TypeORM dans une application NestJS?
Pour configurer l'ORM TypeORM dans votre application NestJS, ouvrez le fichier 'app.module.ts' et importez le module 'TypeOrmModule'. Ensuite, utilisez la méthode 'forRoot' pour configurer les paramètres de connexion à PostgreSQL.
Quels sont les avantages d'utiliser NestJS avec PostgreSQL?
NestJS offre une structure solide et modulaire qui facilite le développement d'applications complexes. L'utilisation de PostgreSQL comme base de données relationnelle permet des requêtes efficaces et des performances élevées, tout en offrant des fonctionnalités avancées telles que les transactions et la gestion des index.

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.