API REST avec Prisma , PostgreSQL et Express
Tags: javascript, express, prisma, postgres
Published: 2024-05-14 18:55:18

**Eh bien,** on dirait que quelqu'un est prêt à créer une API REST de folie ! 🔥 Dans cet article, nous allons plonger dans le monde des bases de données avec PostgreSQL, l'ORM Prisma, et Express pour construire une API REST géniale. 💻

**Mais avant de commencer**, assurons-nous que vous avez tout ce qu'il faut. Node.js ? ✔️ Docker ? ✔️ Une bonne dose d'enthousiasme ? ✔️✔️✔️ Parfait, on est parti ! 🚀


**Première étape :** configurer notre projet TypeScript. Créez un nouveau dossier, initialisez npm, et installez les dépendances nécessaires. C'est comme préparer le terrain de jeu avant la partie. 👷‍♂️

Tout d'abord, créez un nouveau répertoire pour votre projet :

```bash
mkdir affectation-cohortes-machines
```
Ensuite, accédez au répertoire et initialisez un npm projet vide. Notez que l' -y option ici signifie que vous ignorez les invites interactives de la commande. Pour parcourir les invites, supprimez -y de la commande :

```bash
cd affectation-cohortes-machines
npm init -y
```
Cette commande crée un package.json fichier minimal que vous utilisez comme fichier de configuration pour votre npm projet. Vous êtes maintenant prêt à configurer TypeScript dans votre projet.

Exécutez la commande suivante pour une configuration TypeScript simple :

```bash
npm install typescript ts-node @types/node --save-dev
```
Cela installe trois packages en tant que dépendances de développement dans votre projet :

**typescript:** La chaîne d'outils TypeScript.
**ts-node:** Un package pour exécuter des applications TypeScript sans compilation préalable en JavaScript.
**@types/node:** Les définitions de type TypeScript pour Node.js.

La dernière chose à faire est d'ajouter un fichier tsconfig.json  pour garantir que TypeScript est correctement configuré pour l'application que vous allez créer.

Ajoutez le code JSON suivant dans le fichier tsconfig.json que vous creer a la racine de votre projet :

```json
{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}
```
Enregistrez et quittez le fichier.

Cette configuration est une configuration standard et minimale pour un projet TypeScript. Si vous souhaitez en savoir plus sur les propriétés individuelles du fichier de configuration, vous pouvez consulter la documentation TypeScript .

Vous avez configuré votre projet TypeScript simple à l'aide de npm.

**Ensuite,** entrons dans le vif du sujet avec Prisma et PostgreSQL. Configurer Prisma, c'est un jeu d'enfant grâce à la CLI. 🧒 Et pour PostgreSQL, on va le faire tourner avec Docker. Comme ça, on a notre propre base de données à portée de main sans se prendre la tête. 🐳

Dans cette étape, vous allez installer la CLI Prisma , créer votre fichier de schéma Prisma initial , configurer PostgreSQL avec Docker et y connecter Prisma. Le schéma Prisma est le fichier de configuration principal de votre configuration Prisma et contient votre schéma de base de données.

Commencez par installer la CLI Prisma avec la commande suivante :

```bash
npm install prisma --save-dev
```
À titre de bonne pratique, il est recommandé d' installer Prisma CLI localement dans votre projet (plutôt que sous forme d'installation globale). Cette pratique permet d'éviter les conflits de versions si vous avez plusieurs projets Prisma sur votre machine.

Ensuite, vous allez configurer votre base de données PostgreSQL à l'aide de Docker. Créez un nouveau fichier Docker Compose avec la commande suivante :

```bash
nano docker-compose.yml
```
Ajoutez maintenant le code suivant au fichier nouvellement créé :

```yml
version: '3.8'
services:
  db:
    image: postgres:latest
    ports:
      - 5558:5432
    environment:
      POSTGRES_USER: sam_mbabhazi
      POSTGRES_PASSWORD: pass123
      POSTGRES_DB: acm
      DATABASE_URL: postgresql://sam_mbabhazi:pass123@localhost:5558/acm?schema=public

```
Ce fichier Docker Compose configure une base de données PostgreSQL accessible via le port 5432 du conteneur Docker. Les informations d'identification de la base de données sont actuellement définies comme sam_mbabhazi(utilisateur) et your_password(mot de passe). N'hésitez pas à ajuster ces informations d'identification en fonction de votre utilisateur et de votre mot de passe préférés. Enregistrez et quittez le fichier.

Une fois cette configuration en place, lancez le serveur de base de données PostgreSQL avec la commande suivante :

```bash
docker-compose up -d
```
Une fois le serveur de base de données en cours d'exécution, vous pouvez maintenant créer votre configuration Prisma. Exécutez la commande suivante depuis la CLI Prisma :

```bash
npx prisma init
```
Après avoir exécuté la commande, la CLI Prisma crée un nouveau dossier appelé prisma dans votre projet. À l'intérieur, vous trouverez un fichier schema.prisma, qui est le fichier de configuration principal de votre projet Prisma (y compris votre modèle de données). Cette commande ajoute également un fichier .env dotenv à votre dossier racine, où vous définirez l'URL de connexion à votre base de données.

Pour vous assurer que Prisma connaît l'emplacement de votre base de données, ouvrez le .env fichier et ajustez la DATABASE_URL variable d'environnement.

Vous pouvez maintenant mettre à jour la variable d'environnement comme suit :

```env
DATABASE_URL="postgresql://sam_mbabhazi:pass123@localhost:5558/acm?schema=public"

```
**Maintenant,** définissons notre modèle de données dans le schéma Prisma. C'est comme dessiner un plan pour notre base de données. 📐 Une fois que c'est fait, on peut créer les tables correspondantes avec Prisma Migrate. Magie ! ✨

Dans cette étape, vous définirez votre modèle de données dans le fichier de schéma Prisma. Ce modèle de données sera ensuite mappé à la base de données avec Prisma Migrate , qui générera et enverra les instructions SQL pour créer les tables qui correspondent à votre modèle de données. Puisque vous créez une application de gestion d'affectation des apprenants dans des cohortes et d’attribution des machines
, les principales entités de l'application seront apprenants,cohortes,sessions et machines.

Prisma utilise son propre langage de modélisation de données pour définir la forme des données de votre application.
Maintenant, ajoutez-y les définitions de modèle suivantes. Vous pouvez placer les modèles en bas du fichier, juste après le generator client bloc :

```ts
// votre fichier schema.prisma,


// Modèle pour la table Ordinateur
model Ordinateur {
  tag        String      @id @default(cuid()) // Identifiant unique de l'ordinateur
  modele     String      // Modèle de l'ordinateur
  fabriquant String? // Fabriquant de l'ordinateur
  Apprenant  Apprenant[]
}

// Modèle pour la table Session
model Session {
  id      String    @id @default(cuid()) // Identifiant unique de la session
  annee   Int // Année de la session
  type    String // Type de la session
  ville   String // Ville de la session
  Cohorte Cohorte[]
}

// Modèle pour la table Cohorte
model Cohorte {
  code        String      @id // Code unique de la cohorte
  description String // Description de la cohorte
  idSession   String      @map("id_session") // Identifiant de la session associée
  session     Session     @relation(fields: [idSession], references: [id]) // Relation avec la table Session
  Apprenant   Apprenant[]
}

// Modèle pour la table Apprenant
model Apprenant {
  matricule     String     @id @default(cuid()) // Matricule unique de l'apprenant
  prenom        String // Prénom de l'apprenant
  nom           String // Nom de l'apprenant
  postnom       String? // Postnom de l'apprenant
  dateNaissance DateTime? // Date de naissance de l'apprenant
  adresse       String // Adresse de l'apprenant
  email         String // Adresse email de l'apprenant
  telephone     String // Numéro de téléphone de l'apprenant
  tagOrdinateur String     @map("tag_ordinateur") // Tag de l'ordinateur attribué
  codeCohorte   String     @map("code_cohorte") // Code de la cohorte associée
  ordinateur    Ordinateur @relation(fields: [tagOrdinateur], references: [tag]) // Relation avec la table Ordinateur
  cohorte       Cohorte    @relation(fields: [codeCohorte], references: [code]) // Relation avec la table Cohorte
}

```

Une fois ces modèles en place, vous pouvez désormais créer les tables correspondantes dans la base de données à l'aide de Prisma Migrate. Dans votre terminal, exécutez la commande suivante :

```bash
npx prisma migrate dev --name init
```
Cette commande crée une nouvelle migration SQL sur votre système de fichiers et l'envoie à la base de données. L' --name init option fournie à la commande spécifie le nom de la migration et sera utilisée pour nommer le dossier de migration créé sur votre système de fichiers.

Vous pouvez également personnaliser le fichier de migration SQL généré si vous ajoutez l' --create-onlyoption à la prisma migrate dev commande ; par exemple, vous pouvez configurer un déclencheur ou utiliser d'autres fonctionnalités de la base de données sous-jacente.

Au cours de cette étape, vous avez défini votre modèle de données dans votre schéma Prisma et créé les tables de bases de données respectives avec Prisma Migrate. À l'étape suivante, vous installerez Prisma Client dans votre projet afin de pouvoir interroger la base de données.

**Mais attendez,** il y a plus ! Explorons les requêtes du client Prisma dans un petit script. C'est l'occasion de se familiariser avec les différentes façons d'interagir avec notre base de données. 🤖

Prisma Client est un générateur de requêtes généré automatiquement et de type sécurisé que vous pouvez utiliser pour lire et écrire par programme des données dans une base de données à partir d'une application Node.js ou TypeScript. Vous l'utiliserez pour accéder à la base de données au sein de vos routes API REST, en remplaçant les ORM traditionnels, les requêtes SQL simples, les couches d'accès aux données personnalisées ou toute autre méthode de communication avec une base de données.

Dans cette étape, vous installerez Prisma Client et vous familiariserez avec les requêtes que vous pouvez envoyer avec. Avant d'implémenter les routes pour votre API REST dans les étapes suivantes, vous explorerez d'abord certaines des requêtes du client Prisma dans un script simple et exécutable.

Tout d'abord, installez Prisma Client dans votre dossier de projet avec le npm package Prisma Client :

```bash
npm install @prisma/client
```
Ensuite, créez un nouveau répertoire appelé src qui contiendra vos fichiers sources :

```bash
mkdir src
```
Tout d'abord, nous créons un nouveau fichier index.ts dans notre répertoire src. C'est comme ouvrir un nouveau chapitre dans notre aventure de codage. 📖
Ensuite, nous importons le constructeur PrismaClient depuis le package @prisma/client que nous avons préalablement installé. C'est comme inviter notre meilleur ami Prisma à rejoindre la fête ! 🥳
Après ça, nous instancions PrismaClient en appelant son constructeur et en obtenant une instance que nous appelons prisma. C'est notre nouvel assistant personnel qui nous aidera à communiquer avec la base de données. 🤖
Maintenant, la partie amusante commence ! Nous définissons une fonction asynchrone appelée main. C'est comme préparer une scène de théâtre où nous allons faire nos performances Prisma Client. 🎭
À l'intérieur de cette fonction, nous ajouterons nos requêtes Prisma Client. Chaque requête est comme un nouvel acte de notre pièce, où nous interagissons avec la base de données de différentes manières. 🎬
Après avoir préparé notre fonction main, nous l'appelons ! Mais attention, nous devons être prêts à gérer les exceptions potentielles. C'est comme avoir un filet de sécurité pour éviter les chutes pendant notre spectacle. 🤸‍♂️
Enfin, nous nous assurons que Prisma Client ferme correctement toutes les connexions de base de données ouvertes avec prisma.$disconnect(). C'est comme ranger notre scène de théâtre après une performance réussie. 🧹

```ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  // ... vos requêtes Prisma Client iront ici
}

main()
  .catch((e) => console.error(e))
  .finally(async () => await prisma.$disconnect())
```
Passons aux choses sérieuses avec Prisma Client ! 💪 Laissez-moi vous guider à travers ce code 

```ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {

// Créons une nouvelle session !
const session=await prisma.session.create({
    data:{
        ville:"Goma",
        annee:2024,
        type:"presentiel"

    }
})
// Créons une nouvelle cohorte et associons la a la session !
const cohorte=await prisma.cohorte.create({
    data:{
        session:{connect:{id:session.id}},
        code:"A2024",
        description:"Description de la cohorte"
    }
})

// Créons un ordinateur !
const ordinateur=await prisma.ordinateur.create({
    data:{
        modele:"EliteBook",
        fabriquant:'Hp',
        tag:"Ehp1"
    }
})

  // Créons un nouvel apprenant nommé Alice !
  const nouvelApprenant = await prisma.apprenant.create({
    data: {
        codeCohorte:cohorte.code,
        tagOrdinateur:ordinateur.tag,
        prenom: 'Alice',
        nom: 'Merveille',
        email: 'alice@kadeacademy.co',
        adresse:"Goma,du lac n°23",
        telephone:"+243977962085",
      
      
    },
  })
  console.log('Nouvel apprenant créé : ', nouvelApprenant)

  // Récupérons tous les apprenants inscrits !
  const tousLesApprenants = await prisma.apprenant.findMany({
    include: { cohorte:{
        include:{
            session:true
        }
    },ordinateur:true }, // Inclure les détails de la cohorte pour chaque apprenant
  })
  console.log('Tous les apprenants : ')
  console.dir(tousLesApprenants, { depth: null })
}

main()
  .catch((e) => console.error(e)) // Si une erreur se produit, on l'affiche
  .finally(async () => await prisma.$disconnect()) // On ferme la connexion à la base de données
```
Dans ce code amusant, nous utilisons deux requêtes Prisma Client :

create : Nous créons un nouvel enregistrement pour un apprenant nommé Alice. 👩‍🎓 Et bien sûr, nous l'inscrivons dans une cohorte en utilisant une requête de liaison,cohorte est déjà lier a la session. C'est comme rejoindre une nouvelle famille d'apprenants enthousiastes !
findMany : Nous récupérons tous les apprenants existants dans la base de données. 🏫 Mais attendez, il y a plus ! Nous incluons également les détails de la cohorte pour chaque apprenant, comme si nous demandions à chacun d'eux de nous présenter sa famille d'apprentissage.

Après avoir exécuté ce code, nous verrons apparaître notre nouvelle recrue Alice, ainsi que tous les autres apprenants inscrits, prêts à conquérir le monde du code ! 💻🎉
N'oubliez pas de fermer proprement la connexion à la base de données une fois que nous avons terminé.🧹

Exécutez maintenant le script avec la commande suivante :

```bash
npx ts-node src/index.ts
```

Vous avez maintenant utilisé Prisma Client pour lire et écrire des données dans votre base de données. Dans les étapes restantes, vous allez implémenter les routes pour un exemple d’API REST.

**Et pour couronner le tout,** implémentons notre première route API REST avec Express. Comme ça, on pourra accéder à nos données depuis le monde extérieur. 🌍 Ajoutons quelques routes supplémentaires pour créer, lire, mettre à jour et supprimer des données, et notre API sera prête à conquérir le monde ! 🏆

Dans cette étape, vous installerez Express dans votre application. Express est un framework Web populaire pour Node.js que vous utiliserez pour implémenter vos routes API REST dans ce projet. La première route que vous implémenterez vous permettra de récupérer tous les utilisateurs de l'API à l'aide d'une requête GET. Les données de l'apprenant seront récupérées de la base de données à l'aide de Prisma Client.

Installez Express avec la commande suivante :

```bash
npm install express
```
Puisque vous utilisez TypeScript, vous souhaiterez également installer les types respectifs en tant que dépendances de développement. Exécutez la commande suivante pour ce faire :

```
npm install @types/express --save-dev
```
Supprimez maintenant tout le code index.tset remplacez-le par ce qui suit pour démarrer votre API REST :

```ts
import { PrismaClient } from '@prisma/client'
import express from 'express'

const prisma = new PrismaClient()
const app = express()

app.use(express.json())

// ... vos routes API REST iront ici

app.listen(3000, () =>
  console.log('REST API server ready at: http://localhost:3000'),
)
```
Oh, je vois que nous entrons dans les détails techniques ! 🤓 Laissez-moi vous guider à travers ce code avec un peu d'humour et de légèreté. 😊
Tout d'abord, nous importons PrismaClient et express depuis leurs packages npm respectifs. C'est comme inviter deux super-héros à notre fête de codage ! 🦸‍♂️🦸‍♀️
Ensuite, nous instancions PrismaClient en appelant son constructeur, ce qui nous donne une instance nommée prisma. C'est notre nouveau meilleur ami qui va nous aider à communiquer avec la base de données. 🥳
Après ça, nous créons notre application Express en appelant express(). C'est comme avoir un serveur Web personnel qui est prêt à recevoir des invités (requêtes HTTP) à tout moment. 🏡
Mais attendez, il y a plus ! Nous ajoutons le middleware express.json() pour nous assurer que notre serveur puisse comprendre les données JSON correctement. C'est comme apprendre une nouvelle langue pour pouvoir communiquer avec les autres. 🌐
Et enfin, nous démarrons notre serveur sur le port 3000. C'est comme ouvrir les portes de notre maison et dire : "Entrez, entrez, les amis ! Le serveur est prêt à vous accueillir !" 🚪
Maintenant, vous avez une meilleure compréhension de ce code, n'est-ce pas ? 😄 Avec un peu d'humour et d'analogies amusantes, même les concepts techniques peuvent sembler plus légers et accessibles. 🎉

Oh,👨‍💻 Vous voulez organiser votre code de manière plus propre et structurée ? J'adore ce genre de défi ! 💪
Tout d'abord, créons un dossier src/modules/ où nous allons ranger tous nos modules. C'est comme une grande armoire où on range nos affaires par catégorie. 🧰 À l'intérieur, on va créer des sous-dossiers pour apprenant, cohorte, session et ordinateur. Chacun aura son propre espace bien organisé.
Concentrons-nous sur le module apprenant pour commencer. À l'intérieur de ce dossier, créons deux autres sous-dossiers : controller et route. Le dossier controller sera notre quartier général pour gérer toutes les opérations liées aux apprenants. 🏢 Quant au dossier route, c'est là que nous définirons les chemins par lesquels les gens pourront accéder à nos fonctionnalités. 🗺️
Maintenant, dans le dossier controller, créons un fichier apprenant.controller.ts. C'est ici que nous allons coder toutes les fonctions pour interagir avec notre base de données PostgreSQL à l'aide de Prisma. 💾 Voici un exemple de code que nous pourrions y mettre :
```ts

import { prisma } from "../index";
import { Request, Response } from 'express';

const createApprenant = async (req: Request, res: Response) => {
  try {
    const { prenom, nom, postnom, dateNaissance, adresse, email, telephone, tagOrdinateur, codeCohorte } = req.body;
    const nouveauApprenant = await prisma.apprenant.create({
      data: {
        prenom,
        nom,
        postnom,
        dateNaissance,
        adresse,
        email,
        telephone,
        tagOrdinateur,
        codeCohorte
      }
    });
    res.status(200).json(nouveauApprenant);
  } catch (e) {
    res.status(500).json({ error: e });
  }
};

const findMany=async(req:Request,res:Response)=>{ 
  const apprenants= await prisma.apprenant.findMany({
    include:{
        cohorte:{
            include:{
                session:true
            }
        },
        ordinateur:true
    }
})
res.status(200).json(apprenants)
}
// Ajoutez d'autres fonctions pour lire, mettre à jour et supprimer des apprenants ici

export default {
  createApprenant,
  findMany
  // ... autres fonctions
};
```
Ensuite, dans le dossier route, créons un fichier apprenant.route.ts. C'est ici que nous définirons les routes pour accéder aux fonctionnalités de notre module apprenant. 🛣️ Voici un exemple :
```ts
import express from "express";
import apprenantController from "../controllers/apprenant.controller";

const ApprenantRouter = express.Router();

ApprenantRouter.get('/',ApprenantController.findMany)
ApprenantRouter.post("/create", apprenantController.createApprenant);
// Ajoutez d'autres routes pour lire, mettre à jour et supprimer des apprenants ici

export default ApprenantRouter;
```
Et voilà ! Nous avons maintenant une structure bien organisée pour notre module apprenant. 🏡 Vous pouvez répéter ces étapes pour les autres modules comme cohorte, session et ordinateur. Avec cette approche, votre code sera plus facile à maintenir et à étendre à l'avenir. 🌳
N'hésitez pas à ajouter vos propres touches personnelles et à expérimenter. Le plus important, c'est de s'amuser et d'apprendre en cours de route. 🎉
et maintenant dans modifions notre index.ts

```ts
import express from "express";

import PostRouter from "./routes/post.route";
import { PrismaClient } from '@prisma/client'

export const prisma = new PrismaClient()

const app = express();
const port = 3000;

async function main() {
  app.use(express.json());

  // Enregistrer les routes API
  app.use("/api/v1/apprenant", ApprenantRouter);

  // Capturer les routes non enregistrés  
app.all("*", (req, res) => {
    res.status(404).json({ error: `Route ${req.originalUrl} not found` });
  });

  app.listen(port, () => {
    console.log(`Server is listening on port ${port}`);
  });
}

main()
  .then(async () => {
    await prisma.$connect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });
```
Pour tester la route, vous pouvez utiliser la curl commande suivante dans votre deuxième session de terminal :

```bash
curl http://localhost:3000/api/v1/apprenant
```

**Alors,** qu'est-ce que vous en dites ? Prêt à relever le défi et à créer une API REST de ouf ? 💪 Avec Prisma, PostgreSQL et Express dans votre arsenal, rien ne pourra vous arrêter ! 🎉