Coder un système de migration pour NestJS avec mongoose

Prérequis:

  • Un projet NestJS fonctionel
  • Utiliser MongoDB et mongoose

Récemment, j'ai ressenti le besoin pressant de refactorer mon jeu discord.
C'est un projet en cours, loin d'être fini mais je ne supportais plus que les maisons soient appelées homes au lieu de houses.
Oh j'en entends déjà me dire : 'Change les labels affichés, c'est pareil!'.

Je me soucis de la consistance du nommage dans mes projets! Si les maisons sont des appartements, "c'est la porte ouverte à toutes les fenêtres".

Bref, j'enfile ma veste de chercheur et je me lance armé de mon meilleur google-fu.
Aujourd'hui, je vous présente le résultat de mon travail.

J'ai choisi d'utiliser la bibliothèque migrate.
Un outil agnostique de tout type de base de données offrant une logique simple de script de migration et de retour en arrière.
De plus, le statut des migrations peut être stocké sous n'importe quel support (en base, un fichier json, ???).

Assez parlé de moi, laissez-moi vous guider.

Installation migrate

Installons ce coquin!

npm i --save migrate

Crée un dossier pour stocker les scripts de migrations!

On aura besoin de deux dossiers:

mkdir src/migrations
mkdir src/migrations-utils

Le premier contiendra nos scripts de migration et le second sera notre boîte à outils.
Commencons par le second!

Quelques outils

Dans l'intro, nous avons vu que l'outil migrate fait fi du type de nos bases de données.
Nous avons donc besoin d'un script pour nous connecter à mongodb:

import { MongoClient } from 'mongodb';
import { configs } from '../config/configuration';

const MONGO_URL = configs.mongoUrl;

export const getDb = async () => {
  const client: any = await MongoClient.connect(MONGO_URL, { useUnifiedTopology: true });
  return client.db();
};

Cool! Continuons!

Migrate est un outil écrit en javascript.
Et comme nous utilisons Typescript, la meilleur chose à faire est de créer un template déjà connecté à notre mongo.

import { getDb } from '../migrations-utils/db';

export const up = async () => {
  const db = await getDb();
  /*
      La migration va ici.
   */
};

export const down = async () => {
  const db = await getDb();
  /*
      Et ici, c'est pour le retour en arrière.
   */
};

J'ai rencontré quelques soucis avec ts-node/register en utilisant migrate en ligne de commande...
Ce petit script m'a sauvé la mise!
Vas-y! Maintenant! Ajoute-le!

// eslint-disable-next-line @typescript-eslint/no-var-requires
const tsNode = require('ts-node');
module.exports = tsNode.register;

Mise à jour du package.json

Il est temps de mettre à jour le package.json de notre projet afin d'avoir une jolie collection de script facile à utiliser!

Un script pour générer les fichiers de migration

Ajoutons cette ligne dans la section script du fichier package.json

"migrate:create": "migrate create --template-file ./src/migrations-utils/template.ts --migrations-dir=\"./src/migrations\" --compiler=\"ts:./src/migrations-utils/ts-compiler.js\"",

--template-file ./src/migrations-utils/template.ts fourni le template, simple et efficace pour notre projet en typescript.
Le template est une façon rapide de créer la base de nos script de migration!

--migrations-dir=\"./src/migrations\" Explique à migrate où sont sensés être les scripts de migration.
Par défaut, c'est à la racine du projet...

--compiler=\"ts:./src/migrations-utils/ts-compiler.js\" Explique à migrate comment gérer les fichiers typescript.

Il suffit de lancer cette commande pour créer une migration vide en typescript dans le bon dossier!

npm run migrate:create -- <migration name>

Un script pour les migrations et un script pour revenir en arrière!

EEEEEeeet deux lignes de plus dans le package.json, toujours dans la section script!

"migrate:up": "migrate --migrations-dir=\"./src/migrations\" --compiler=\"ts:./src/migrations-utils/ts-compiler.js\" up",
"migrate:down": "migrate --migrations-dir=\"./src/migrations\" --compiler=\"ts:./src/migrations-utils/ts-compiler.js\" down"

Pas de nouvelle option ici, tout est expliqué au-dessus mais comme je suis sympa voila un petit rappel.

--migrations-dir=\"./src/migrations\" Explique à migrate où sont nos scripts de migration...

--compiler=\"ts:./src/migrations-utils/ts-compiler.js\" Explique à migrate comment gérer les fichiers typescript...

On peut maintenant exécuter le script npm run migrate:up pour les migrations et le script npm run migrate:down pour revenir en arrière.

Mais que se passe-t-il quand nous exécutons une migration?

Migrate va stocker l'état des migrations dans un fichier à la racine du projet.
Ce fichier est: migrate.json.
Il ressemble à ça:

{
  "lastRun": "1605197159478-test.ts",
  "migrations": [
    {
      "title": "1605197159478-test.ts",
      "timestamp": 1605197181474
    }
  ]
}

NE PAS COMMITER migrate.json

Questions?

Je serais heureux de répondre aux questions en commentaires.

Si cette article t'as plu, n'hésite pas à me rejoindre sur le discord Webeleon!
☎️Discord webeleon

Tu peux aussi me contacter par mail si tu cherches un freelance sympa et efficace 💰
✉️Email moi!

Et comme je suis partageur, tu trouveras les sources sur github!
🎁Récupère le code sur github!

Documentation

21