Configuration complète des migrations de TypeORM avec NESTJS en TypeScript.

Bonjour, je suppose que si vous êtes arrivé ici, c'est parce que comme moi, vous avez eu du mal à configurer la migration TypeORM avec NestJS. Voici comment le faire en 5 minutes.
NestJS est un framework Node.js utilisé pour construire des API scalables, efficaces et maintenables. Il utilise des fonctionnalités modernes de JavaScript telles que les décorateurs et fournit une architecture modulaire qui facilite l'organisation du code. TypeORM est une bibliothèque de Mapping Objet-Relationnel (ORM) qui simplifie la gestion des bases de données en mappant les tables de la base de données aux classes et aux objets. Ensemble, NestJS et TypeORM facilitent la construction et la gestion d'applications complexes avec des bases de données.
Dans un premier temps, installons la librairie TypeOrm pour NestJs :
1
2
3
4
npm install --save @nestjs/typeorm typeorm
# add at the end of this command your database driver :
# pg (for postgres db driver) sqlite3 (for sqlite 3 db driver) ...
# I personally used pg for this example1 : Créez le fichier de configuration de TypeOrm appelé "data-sources.ts" :
1
2
3
4
5
6
7
8
9
10
11
12
import { DataSource, DataSourceOptions } from "typeorm";
export const dataSourceOptions: DataSourceOptions = {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'postgres',
database: 'avl',
migrations: ["dist/migrations/*.js"],
}
const dataSource = new DataSource(dataSourceOptions)
export default dataSourceDans ce code, nous pouvons déjà voir que les fichiers de migration que nous allons cibler se terminent par ".js". La raison en est que TypeOrm fonctionne dans un environnement JavaScript, il fonctionnera donc sur nos fichiers TypeScript une fois compilés en JavaScript.
Comme vous l'avez peut-être remarqué, les fichiers de migration sont ensuite placés dans le répertoire "dist", qui est le répertoire de destination des fichiers émis par compilateur TypeScript.
Donc, ce que vous devez vérifier dans un premier temps est que "outDir" soit égal à "./dist" dans votre fichier tsconfig.json :
1
2
3
4
"compilerOptions": {
...,
"outDir": "./dist",
},2 : Importez cette configuration dans le fichier app.module.ts :
1
2
3
4
5
6
7
8
@Module({
imports: [
TypeOrmModule.forRoot(dataSourceOptions),
UsersModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }TypeOrmModule.forRoot est une méthode qui configure le module TypeORM avec en root. Lorsqu'elle est appelée dans le fichier app.module.ts, cette méthode connecte à la base de données avec la configuration et rend le module TypeORM disponible dans toute l'application.
3 : Créez votre premier fichier de migration en tapant la commande suivante :
1
typeorm migration:create ./migrations/TestNous appelons le fichier de migration "Test" et nous le voulons dans le répo "migrations" à la racine
Par défault, le fichier aura l'allure suivante :
1
2
3
4
5
6
7
export class Test1677858325330 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
}
async down(queryRunner: QueryRunner): Promise<void> {
}
}La méthode up est utilisée pour définir les modifications qui doivent être apportées au schéma de la base de données pour appliquer la migration, tandis que la méthode down est utilisée pour définir comment annuler ces modifications au cas où la migration doit être annulée. La méthode up doit contenir des requêtes SQL ou des commandes TypeORM qui modifient le schéma de la base de données, tandis que la méthode down doit contenir les commandes opposées pour annuler ces modifications.
Nous pouvons maintenant ajouter quelques datas pour créer des tables dans notre base de données. Pour l'éxemple, j'ai pris des données directement de la doc de TypeORM.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import {
MigrationInterface,
QueryRunner,
Table,
TableIndex,
TableColumn,
TableForeignKey,
} from "typeorm"
export class Test1677858325330 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: "question",
columns: [
{
name: "id",
type: "int",
isPrimary: true,
},
{
name: "name",
type: "varchar",
},
],
}),
true,
)
await queryRunner.createIndex(
"question",
new TableIndex({
name: "IDX_QUESTION_NAME",
columnNames: ["name"],
}),
)
await queryRunner.createTable(
new Table({
name: "answer",
columns: [
{
name: "id",
type: "int",
isPrimary: true,
},
{
name: "name",
type: "varchar",
},
{
name: "created_at",
type: "timestamp",
default: "now()",
},
],
}),
true,
)
await queryRunner.addColumn(
"answer",
new TableColumn({
name: "questionId",
type: "int",
}),
)
await queryRunner.createForeignKey(
"answer",
new TableForeignKey({
columnNames: ["questionId"],
referencedColumnNames: ["id"],
referencedTableName: "question",
onDelete: "CASCADE",
}),
)
}
async down(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("answer")
const foreignKey : any = table?.foreignKeys.find(
(fk) => fk.columnNames.indexOf("questionId") !== -1,
)
await queryRunner.dropForeignKey("answer", foreignKey)
await queryRunner.dropColumn("answer", "questionId")
await queryRunner.dropTable("answer")
await queryRunner.dropIndex("question", "IDX_QUESTION_NAME")
await queryRunner.dropTable("question")
}
}4 : Configurez votre commande TypeORM de génération de fichiers de migration dans package.json :
Lorsque vous utilisez TypeScript, le code source est écrit en TypeScript, mais il doit être compilé en JavaScript avant de pouvoir être exécuté. Les fichiers JavaScript résultants sont stockés dans le répertoire "dist", qui signifie "distribution". Ce répertoire contient la version compilée du code source, qui est réellement exécuté par le runtime Node.js.
4.1: Ouvrez votre fichier "package.json" et ajoutez-y les lignes suivantes dans scripts :
1
2
3
4
5
6
7
8
"scripts": {
...,
"typeorm": "npm run build && npx typeorm -d dist/data-source.js",
"migration:generate": "npm run typeorm -- migration:generate",
"migration:run": "npm run typeorm -- migration:run",
"migration:revert": "npm run typeorm -- migration:revert"
},La commande "typeorm" construit dans un premier temps le projet, puis exécute la commande npx typeorm en spécifiant le chemin du fichier de configuration compilé dans le répo "dist".
4.2: Assurez-vous que TypeScript va comilier tous les fichiers de migration :
Retournez dans le fichier "tsconfig.json" et assurez-vous que le répo "migrations" est unclus dans le compiler coverage :
1
2
3
4
"include": [
"./src/**/*",
"migrations/**/*.ts"
]Pour s'assurer que tout a bien été compilé, lancez la commande suivante :
1
npm run buildCette commande va créer le répo "dist" et va insérer à l'intérieur le fichier "data-sources.js" ainsi que les fichiers de migrations. Tous ces fichiers sont maintenant en .js.
5 : Une fois que tout est prêt dans le dossier "dist", vous êtes prêt pour lancer la migration :
1
npm run typeorm migration:runCette commande devrait créer une table "migration" qui réfère aux migrations que vous avez effectué. Vous y trouverez aussi les tables que vous avez souhaité créer dans votre fichier de migration.
Conclusion :
En conclusion, NestJS et TypeORM fournissent des outils puissants pour créer des applications Node.js évolutives et maintenables. L'utilisation conjointe de ces outils peut grandement simplifier le processus de création et de gestion des migrations de bases de données, ce qui facilite la synchronisation de votre schéma de base de données avec votre code d'application. En suivant les étapes décrites dans cet article, vous pouvez créer des fichiers de migration qui peuvent être exécutés sur votre base de données avec seulement quelques commandes simples. Grâce à ces connaissances, vous pouvez apporter en toute confiance des modifications au schéma de votre base de données à mesure que votre application évolue, sachant que vous disposez d'un processus fiable et reproductible pour gérer ces modifications. PS : vous pouvez créer vos fichiers « data-source.ts » et migrations où vous voulez dans votre projet. Assurez-vous simplement que le compilateur TypeScript les inclut. J'ai volontairement mis ces fichiers à la racine pour vous montrer l'ensemble du processus de configuration, mais par défaut, ces éléments se trouvent dans le src/repo