Passer au contenu

SQL (TypeORM)

Ce chapitre s’applique uniquement à TypeScript

TypeORM est sans doute le Mapper Relationnel Objet (ORM) le plus mature disponible dans le monde Node.js. Puisqu’il est écrit en TypeScript, il fonctionne très bien avec le framework Nest.

Prise en main

Pour commencer cette aventure avec cette bibliothèque, nous devons installer toutes les dépendances requises :

Fenêtre de terminal
$ npm install --save typeorm mysql2

La première étape consiste à établir la connexion avec notre base de données en utilisant la classe new DataSource().initialize() importée depuis le package typeorm. La fonction initialize() retourne une Promise, et par conséquent, nous devons créer un fournisseur asynchrone.

database.providers.ts
import { DataSource } from 'typeorm';
export const databaseProviders = [
{
provide: 'DATA_SOURCE',
useFactory: async () => {
const dataSource = new DataSource({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [
__dirname + '/../**/*.entity{.ts,.js}',
],
synchronize: true,
});
return dataSource.initialize();
},
},
];

Ensuite, nous devons exporter ces fournisseurs pour les rendre accessibles au reste de l’application.

database.module.ts
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}

Maintenant, nous pouvons injecter l’objet DATA_SOURCE en utilisant le décorateur @Inject(). Chaque classe qui dépendra du fournisseur asynchrone DATA_SOURCE attendra jusqu’à ce qu’une Promise soit résolue.

Modèle de dépôt

TypeORM prend en charge le modèle de conception de dépôt, donc chaque entité a son propre dépôt. Ces dépôts peuvent être obtenus à partir de la connexion à la base de données.

Mais d’abord, nous avons besoin d’au moins une entité. Nous allons réutiliser l’entité Photo de la documentation officielle.

photo.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 500 })
name: string;
@Column('text')
description: string;
@Column()
filename: string;
@Column('int')
views: number;
@Column()
isPublished: boolean;
}

L’entité Photo appartient au répertoire photo. Ce répertoire représente le PhotoModule. Maintenant, créons un fournisseur Repository :

photo.providers.ts
import { DataSource } from 'typeorm';
import { Photo } from './photo.entity';
export const photoProviders = [
{
provide: 'PHOTO_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(Photo),
inject: ['DATA_SOURCE'],
},
];

Nous pouvons maintenant injecter le Repository<Photo> dans le PhotoService en utilisant le décorateur @Inject() :

photo.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Photo } from './photo.entity';
@Injectable()
export class PhotoService {
constructor(
@Inject('PHOTO_REPOSITORY')
private photoRepository: Repository<Photo>,
) {}
async findAll(): Promise<Photo[]> {
return this.photoRepository.find();
}
}

La connexion à la base de données est asynchrone, mais Nest rend ce processus complètement invisible pour l’utilisateur final. Le PhotoRepository attend la connexion à la base de données, et le PhotoService est retardé jusqu’à ce que le dépôt soit prêt à être utilisé. L’application entière peut démarrer lorsque chaque classe est instanciée.

Voici un module PhotoModule final :

photo.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { photoProviders } from './photo.providers';
import { PhotoService } from './photo.service';
@Module({
imports: [DatabaseModule],
providers: [
...photoProviders,
PhotoService,
],
})
export class PhotoModule {}