Configuration du monorepo

Nous configurons un package.json à la racine du projet et installons Turborepo, pour orchestrer l'exécution de nos deux projets.

6 min read

Configuration du monorepo

Pour configurer le monorepo, nous avons besoin de reculer d'un dossier.

Terminal
1
cd .. # Pour remonter d'un dossier, si vous êtes dans le dossier frontend

À l'intérieur du dossier stack-remix-nestjs, nous allons instancier un nouveau fichier package.json pour configurer notre monorepo.

Terminal
1
npm init -y # Pour créer un fichier package.json

Le fichier package.json doit ressembler à ça :

package.json
1
{
2
"name": "nestjs-remix-monorepo",
3
"version": "1.0.0",
4
"description": "",
5
"main": "index.js",
6
"scripts": {
7
"test": "echo \"Error: no test specified\" && exit 1"
8
},
9
"keywords": [],
10
"author": "",
11
"license": "ISC",
12
"packageManager": "npm@10.2.3"
13
}

Installation de Turbo

Nous allons utiliser une librairie appelée Turborepo pour gérer notre monorepo.

Terminal
1
npm install -D turbo

Nous pouvons aussi l'installer en global pour bénéficier de la commande turbo dans le terminal.

Terminal
1
npm install -g turbo

Ensuite, nous allons déclarer nos deux projets comme des workspaces dans le fichier package.json.

Le fichier devrait maintenant ressembler à ça :

package.json
1
{
2
"name": "nestjs-remix-monorepo",
3
"version": "1.0.0",
4
"description": "",
5
"main": "index.js",
6
"scripts": {
7
"test": "echo \"Error: no test specified\" && exit 1"
8
},
9
"keywords": [],
10
"author": "",
11
"license": "ISC",
12
"packageManager": "npm@10.2.3",
13
"workspaces": ["frontend", "backend"],
14
"devDependencies": {
15
"turbo": "^1.13.0"
16
}
17
}

Création du fichier turbo.json

Nous devons ensuite créer un fichier de configuration Turbo. Pour ce faire, nous pouvons créer un fichier nommé turbo.json.

Terminal
1
touch turbo.json # Pour créer un fichier turbo.json

Ensuite, nous allons coller la configuration par défaut, qu'on retrouve sur la documentation officielle :

turbo.json
1
{
2
"$schema": "https://turbo.build/schema.json",
3
"pipeline": {
4
"build": {
5
"outputs": ["dist/**"]
6
},
7
"type-check": {}
8
}
9
}

(Optionnel) Création d'un fichier .gitignore

Nous pouvons ajouter un fichier .gitignore pour éviter de versionner les fichiers volumineux.

Terminal
1
touch .gitignore # Pour créer un fichier .gitignore
1
.turbo
2
node_modules
3
/dist
4
/build
5
/out

Création de pipelines avec Turborepo

Les pipelines permettent de définir des commandes que turbo va exécuter pour chaque projet.

Exemple : Nous allons créer une pipeline nommée dev pour exécuter l'application NestJS en environnement de développement.

D'abord, nous devons faire une petite modification aux fichiers package.json respectifs de chaque projet.

Nous allons ajouter trois commandes dans le fichier backend/package.json de notre application NestJS.

backend/package.json
1
"scripts": {
2
"dev": "run-p dev:compile dev:watch",
3
"dev:compile": "tsc --build --watch",
4
"dev:watch": "nodemon node dist/main.js",
5
}

Explication : La première commande va exécuter en parallèle les deux commandes suivantes.

dev:compile va compiler le code source de NestJS en continu. dev:watch va surveiller les changements apportés au code et redémarrer le serveur à chaque modification.

Pour ce faire, nous devons également installer les dépendances nodemon et npm-run-all.

Terminal
1
cd backend # Pour se déplacer dans le dossier backend
2
npm install -D nodemon npm-run-all # Pour installer les dépendances

Ensuite, renommons la commande devpar old-dev dans le frontend/package.json pour l'empêcher de s'exécuter.

frontend/package.json
1
"scripts": {
2
"old-dev": "remix vite:dev"
3
}

Nous devons maintenant créer une pipeline dev dans notre fichier de configuration turbo.json

turbo.json
1
{
2
"$schema": "https://turbo.build/schema.json",
3
"pipeline": {
4
"build": {
5
"outputs": ["dist/**"]
6
},
7
"dev": {
8
"cache": false,
9
"persistent": true
10
}
11
}
12
}

Pour exécuter cette pipeline, nous devons lancer la commande turbo dans le terminal, suivi du nom de la pipeline.

Terminal
1
turbo dev

Nous allons créer une commande dans le fichier package.json de la racine pour exécuter cette pipeline.

package.json
1
"scripts": {
2
"dev": "turbo dev"
3
}

Renommer la librairie frontend pour l'importer dans le backend

Maintenant, lancer la commande npm run dev dans le terminal exécutera l'application NestJS (qui s'occupera d'exécuter Remix.js).

On rencontre l'erreur suivante.

Erreur lors du lancement de notre application NestJS

Pas de panique, c'est normal. Nous n'avons pas encore importé les méthodes définies dans le fichier frontend/index.cjs.

Pour pouvoir importer ces méthodes, nous devons déclarer la librairie @virgile/frontend dans le fichier backend/package.json.

D'abord, renommons nos projets backend/package.json et frontend/package.json.

backend/package.json
1
"name": "@virgile/backend",
frontend/package.json
1
"name": "@virgile/frontend",

Ces noms vont nous permettre d'utiliser la librairie @virgile/frontend dans notre application NestJS.

Après avoir fait ce changement, il nous suffit d'ajouter la dépendance @virgile/frontend en tant que dependencies dans le fichier backend/package.json.

backend/package.json
1
"dependencies": {
2
"@virgile/frontend": "*" // Nous informons le monorepo qu'il doit utiliser notre librairie frontend dans le projet backend
3
}

Attention. Comme nous venons de renommer notre librairie frontend, nous devons nous assurer d'utiliser le même nom dans le fichier frontend/index.d.cts

frontend/index.d.cts
1
declare module '@virgile/backend' {
2
// <= Nom du module, à adapter selon le nom de votre projet
3
export function getPublicDir(): string;
4
export function getServerBuild(): Promise<any>;
5
export function startDevServer(app: any): Promise<void>;
6
}

Importer getServerBuild dans RemixController

Nous pouvons à présent importer la méthode getServerBuild dans le fichier backend/src/remix/remix.controller.ts.

Voici le fichier modifié :

backend/src/remix/remix.controller.ts
1
import { All, Controller, Next, Req, Res } from '@nestjs/common';
2
import { createRequestHandler } from '@remix-run/express';
3
import { getServerBuild } from '@virgile/frontend';
4
import { NextFunction, Request, Response } from 'express';
5
6
@Controller()
7
export class RemixController {
8
@All('*')
9
async handler(
10
@Req() request: Request,
11
@Res() response: Response,
12
@Next() next: NextFunction
13
) {
14
//
15
return createRequestHandler({
16
build: await getServerBuild(),
17
getLoadContext: () => ({
18
toto: 'Salut, ça va ?',
19
}),
20
})(request, response, next);
21
}
22
}

Utiliser les méthodes getPublicDir et startDevServer

Il nous manque deux méthodes à utiliser avant de pouvoir utiliser Remix.js.

Nous allons importer les méthodes getPublicDir et startDevServer dans le fichier backend/src/main.ts.

backend/src/main.ts
1
import { NestFactory } from '@nestjs/core';
2
import { NestExpressApplication } from '@nestjs/platform-express';
3
import { getPublicDir, startDevServer } from '@virgile/frontend';
4
import { AppModule } from './app.module';
5
6
async function bootstrap() {
7
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
8
bodyParser: false,
9
});
10
11
await startDevServer(app);
12
13
app.useStaticAssets(getPublicDir(), {
14
immutable: true,
15
maxAge: '1y',
16
index: false,
17
});
18
19
const selectedPort = process.env.PORT ?? 3000;
20
21
console.log(`Running on port http://localhost:${selectedPort}`);
22
await app.listen(selectedPort);
23
}
24
bootstrap();

Nous exécutons la méthode startDevServer, qui va démarrer le serveur de développement de Remix.js en utilisant Vite. On n'aura donc pas besoin d'exécuter deux serveurs en parallèle, NestJS s'occupe de tout.

Ensuite, nous déclarons le dossier public de Remix.js comme dossier statique pour que NestJS puisse servir les fichiers statiques (comme les images, le favicon, le CSS ...).

Lancer le serveur de développement

Nous pouvons à présent lancer le serveur de développement en exécutant la commande npm run dev dans le terminal.

Terminal
1
npm run dev

Le serveur devrait démarrer sans erreur.

Notre serveur NestJS tourne sans erreur.