Maîtriser NestJS : connecter NestJS à une base PostgreSQL
6 septembre 2023 · 5 min read · Read in English
Sommaire
Introduction
Dans le monde en constante évolution du développement web, une connexion à la base de données fiable et efficace est cruciale. Dans notre exploration continue de NestJS, on a vu ses composants essentiels comme les modules, contrôleurs et services. Il est temps d'aborder un élément vital pour développer des applications backend solides : utiliser TypeORM pour connecter NestJS à une base PostgreSQL.
Dans cet article, on parcourt ensemble les étapes pour connecter ton application NestJS à une base PostgreSQL. On verra tout : configuration de la base, définition des entités, exécution des requêtes, opérations sur la base. À la fin, tu auras les connaissances et compétences pour exploiter pleinement les capacités de NestJS avec PostgreSQL dans ton projet.
J'ai créé un dépôt GitHub pour cette série, accessible à l' adresse suivante.
Installer les packages requis
NestJS fournit le package @nestjs/typeorm pour se connecter aux bases SQL. TypeORM est l'ORM
le plus avancé pour TypeScript. Il fonctionne bien avec le framework Nest parce qu'il est écrit
en TypeScript. L'approche présentée dans ce chapitre s'applique à toute base supportée par
TypeORM. Tu n'as qu'à installer les bibliothèques client API pour la base choisie.
Pour commencer, on doit installer les dépendances requises :
npm install --save @nestjs/typeorm typeorm pg
Une fois l'installation terminée, on peut importer le TypeOrmModule dans le module racine
AppModule.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProductsModule } from './products/products.module';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
ProductsModule,
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'root',
password: '',
database: 'awesome-nestjs',
autoLoadEntities: true,
synchronize: true, // avoid doing this in production please and use migrations instead
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
}
Voilà ce que propose la doc officielle. On y reviendra un peu plus tard pour quelques améliorations.
Entités
Dans l'article précédent on a parlé des entités. Cette fois, j'aimerais y revenir parce qu'il y a quelques petits ajustements à faire. Je te montre de quoi je parle :
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
// @Entity annotation is used to define our Product class as an entity
// products simply represent the table name (it's optional).
@Entity('products')
export class Product {
// this anotation is used to define a property as the primary key.
// it will also make sure the PK is auto increment! pretty cool hien ???
@PrimaryGeneratedColumn()
id: number;
@Column() // simply define a property as a table column
name: string;
@Column({ nullable: true }) // simply means that the field will can be nullable in the table
description?: string;
@Column()
category: string;
@Column()
price: number;
@Column()
image: string;
}
Pattern Repository
Le pattern Repository est un design pattern courant qui aide à isoler la logique métier de ton application des opérations sous-jacentes sur la base de données. Dans NestJS, tu peux appliquer cette approche en douceur avec TypeORM, en obtenant une manière propre et organisée de te connecter à ta base.
Mettons à jour le fichier du module produit en y important l'entité Product :
import { Module } from '@nestjs/common';
import { ProductsService } from './products.service';
import { ProductsController } from './products.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Product } from './entities/product.entity';
@Module({
imports: [TypeOrmModule.forFeature([Product])],
controllers: [ProductsController],
providers: [ProductsService],
})
export class ProductsModule {
}
Ce module utilise la méthode forFeature() pour préciser quels repositories sont enregistrés
dans le scope courant. Avec ça en place, on peut utiliser le décorateur @InjectRepository()
pour injecter le UsersRepository dans le ProductsService :
import { Injectable } from '@nestjs/common';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Product } from './entities/product.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class ProductsService {
constructor(
@InjectRepository(Product)
private productRepository: Repository<Product>,
) {
}
async create(createProductDto: CreateProductDto) {
// The save method is used to persist an entity, either by creating a new record in the database or updating an existing one
return await this.productRepository.save(createProductDto);
}
async findAll(): Promise<Product[]> {
// with the find method, we can laod all the products (an array of products)
return this.productRepository.find();
}
async findOne(id: number): Promise<Product | null> {
// the findoneBy method allow to retrieve the first row that matches with the provided id
// it could be name, or product reference, but you have to make sure the field is unique
return await this.productRepository.findOneBy({ id });
}
async update(id: number, updateProductDto: UpdateProductDto) {
// the update method modify an existing record
return await this.productRepository.update(id, updateProductDto);
}
async remove(id: number) {
// to delete a row by it's id we use delete method
return await this.productRepository.delete(id);
}
}
Variables d'environnement
Les applications tournent fréquemment dans différents contextes. Différentes options de configuration doivent être utilisées selon l'environnement. Par exemple, l'environnement local repose typiquement sur des credentials de base de données spécifiques à l'instance locale. L'environnement de production a ses propres credentials. Comme ces variables changent, il est recommandé de les conserver dans l'environnement.
Trêve de bavardages, installons les modules Node nécessaires :
npm i --save @nestjs/config
Maintenant, on crée un fichier .env qui sera chargé automatiquement au démarrage du projet :
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=root
POSTGRES_PASSWORD=password
POSTGRES_DB=awesome-nestjs
N'oublie pas de mettre à jour avec tes vraies infos de base de données.
Ok, mettons à jour notre app.module.ts pour profiter des fonctionnalités de la config Nest :
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProductsModule } from './products/products.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ cache: true }),
ProductsModule,
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get<string>('POSTGRES_HOST'),
port: configService.get<number>('POSTGRES_PORT'),
username: configService.get<string>('POSTGRES_USER'),
password: configService.get<string>('POSTGRES_PASSWORD'),
database: configService.get<string>('POSTGRES_DB'),
autoLoadEntities: true,
synchronize: true,
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
}
Voilà, on s'est facilité la vie avec le package nest/configuration. Mais je ne sais pas si tu
es aussi maniaque que moi de la validation des données. Bref, je te montre comment valider les
valeurs d'un fichier .env.
Validation de schéma
Si les variables d'environnement requises ne sont pas fournies ou ne respectent pas certaines règles de validation, l'usage est de lever une exception au démarrage du programme.
Gérons ça rapidement :
npm install --save joi
On peut définir un schéma de validation Joi et le passer via la propriété validationSchema
de l'objet d'options de forRoot(), comme ci-dessous :
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProductsModule } from './products/products.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as Joi from 'joi';
@Module({
imports: [
ProductsModule,
ConfigModule.forRoot({
cache: true,
validationSchema: Joi.object({
POSTGRES_HOST: Joi.string().required(),
POSTGRES_PORT: Joi.number().required(),
POSTGRES_USER: Joi.string().required(),
POSTGRES_PASSWORD: Joi.string().required(),
POSTGRES_DB: Joi.string().required(),
}),
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get<string>('POSTGRES_HOST'),
port: configService.get<number>('POSTGRES_PORT'),
username: configService.get<string>('POSTGRES_USER'),
password: configService.get<string>('POSTGRES_PASSWORD'),
database: configService.get<string>('POSTGRES_DB'),
autoLoadEntities: true,
synchronize: true,
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
}
Crois-moi : prends l'habitude de valider le fichier
.envpour éviter les soucis en production.
Résumé
Dans cet article, on a relié NestJS à PostgreSQL, une base de données relationnelle open source. On a appris à créer une connexion fluide entre notre application NestJS et la base via TypeORM, posant les bases d'applications data-driven efficaces et maintenables.
On a couvert des tâches essentielles : créer la connexion à la base, définir les entités, et exécuter des requêtes pour les opérations CRUD. On en a aussi appris davantage sur la façon dont NestJS et TypeORM coopèrent pour simplifier les interactions avec la base, rendant la conception d'APIs scalables et performantes plus simple que jamais.
Dans le prochain article, on verra ensemble comment implémenter les relations OneToOne, OneToMany et ManyToMany avec TypeORM et PostgreSQL.
À suivre !
S'abonner aux prochains articles
Recevez les nouveaux articles par e-mail. Pas de spam, désinscription à tout moment.
Related posts
Maîtriser NestJS : libérer la puissance des relations avec TypeORM et les bases SQL
Libère la puissance des relations de données avec NestJS, TypeORM et les bases SQL. Maîtrise l'art de construire des structures de données complexes et des interactions fluides. Idéal pour les développeurs NestJS expérimentés comme pour les débutants qui veulent créer des applications à la pointe.
October 18, 2023
Maîtriser NestJS : comprendre Services, Modules et Contrôleurs
Apprends les composants essentiels de NestJS pour construire des applications serveur scalables. Examine le rôle des services, modules et contrôleurs en exploitant les capacités des DTO (Data Transfer Objects) pour la validation et le renforcement de la sécurité.
September 3, 2023
Maîtriser NestJS : ton guide ultime du développement backend moderne
Explore NestJS, un framework Node.js moderne, dans cette introduction complète. Rejoins notre série pour maîtriser l'architecture modulaire, les contrôleurs, providers, services, et plus encore. Construis des applications backend scalables en toute confiance. Bienvenue dans la partie 1 de la série NestJS !
August 23, 2023