Aller au contenu

Prisma ORM

Qu'est-ce qu'un ORM

Un ORM (de l'anglais Object-Relational Mapping) sert à simplifier l'interface entre la BD et le code fonctionnel. Au lieu de faire des requêtes SQL directement dans le code (comme des prepare en PHP), on va utiliser des fonctions typescript.

Sans ORM (SQL brut) Avec ORM (Prisma)
SELECT * FROM produit WHERE id = 1 prisma.produit.findUnique({ where: { id: 1 } })
INSERT INTO produit (nom, prix) VALUES ('Clavier', 129.99) prisma.produit.create({ data: { nom: 'Clavier', prix: 129.99 } })

Il existe plusieurs modules ORM, mais Prisma est largement utilisé. Dans le cours, nous l'utiliserons avec MySQL.

Installation et configuration

Installer Prisma

console
npm install prisma --save-dev
npm install @prisma/client @prisma/adapter-mariadb

Initialiser Prisma avec MySQL

console
npx prisma init --datasource-provider mysql

On se retrouve avec :

  • Un dossier prisma/ contenant un fichier schema.prisma
  • Un fichier .env avec la variable DATABASE_URL

Configurer la connexion à la base de données

Modifiez le fichier .env avec vos informations de connexion MySQL :

.env
DATABASE_URL="mysql://utilisateur:motdepasse@localhost:3306/nom_de_la_bd"
DATABASE_HOST="localhost"
DATABASE_USER="utilisteur"
DATABASE_PASSWORD="motdepasse"
DATABASE_NAME="nom_de_la_bd"

Schéma Prisma

Un des fichiers importants pour bien utiliser l'ORM est schema.prisma : c'est là qu'on décrit la BD.

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
}

model Produit {
  id          Int       @id @default(autoincrement())
  nom         String
  description String?
  prix        Float
  categorieId Int
  categorie   Categorie @relation(fields: [categorieId], references: [id])
  creeLe      DateTime  @default(now())
  modifieLe   DateTime  @updatedAt
}

model Categorie {
  id       Int       @id @default(autoincrement())
  nom      String    @unique
  produits Produit[]
}

Écrire ce fichier peut être difficile pour un débutant, le truc est de faire sa BD en SQL et extraire la BD dans Prisma :

console
npx prisma db pull

Types de données courants

Type Prisma Type MySQL Description
String VARCHAR(191) Texte
Int INT Nombre entier
Float DOUBLE Nombre décimal
Boolean TINYINT(1) Vrai ou faux
DateTime DATETIME Date et heure

Attributs courants

Attribut Description
@id Clé primaire
@default(autoincrement()) Auto-incrémentation
@default(now()) Date actuelle par défaut
@updatedAt Mis à jour automatiquement
@unique Valeur unique
? après le type Champ optionnel (nullable)

Migrations

Durant la vie de votre application, il est presque certain que la BD va être changée. C'est là que la puissance de Prisma joue un rôle critique. Si vous modifiez schema.prisma, vous pouvez automatiser la mise à jour de la BD avec les migrations.

Créer et appliquer une migration

console
npx prisma migrate dev --name init

En arrière-plan, Prisma :

  1. Compare le schéma avec l'état actuel de la base de données
  2. Génère le fichier SQL correspondant à la différence
  3. Exécute ce SQL sur la base de données
  4. Régénère le Prisma Client pour que le code TypeScript reflète le nouveau schéma

Réinitialiser la base de données

Pour avoir une BD vide, on peut faire une réinitialisation complète :

console
npx prisma migrate reset

Prisma Client

Le schéma décrit la structure de la base de données, mais le client est l'interface de programmation à utiliser dans votre code. Pour générer le client, faire la commande suivante :

console
npx prisma generate

Configuration du client

Voici le code pour créer une instance de prisma en singleton, utile pour les fonctions plus bas.

lib/prisma.ts
import { PrismaClient } from "@/app/generated/prisma/client";
import { PrismaMariaDb } from "@prisma/adapter-mariadb";

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
};

const adapter = new PrismaMariaDb(process.env.DATABASE_URL!);

export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });

if (process.env.NODE_ENV !== "production") {
  globalForPrisma.prisma = prisma;
}

Opérations CRUD

Voici des exemples de CRUD pour produit.

Créer (Create)

Créer un enregistrement
const produit = await prisma.produit.create({
  data: {
    nom: "Clavier mécanique",
    description: "Clavier RGB",
    prix: 129.99,
    categorieId: 1,
  },
});

Lire (Read)

Lire des enregistrements
// Tous les produits
const produits = await prisma.produit.findMany();

// Un produit par son ID
const produit = await prisma.produit.findUnique({
  where: { id: 1 },
});

// Produits avec leur catégorie (jointure)
const produitsAvecCategorie = await prisma.produit.findMany({
  include: { categorie: true },
});

// Produits filtrés
const produitsElectroniques = await prisma.produit.findMany({
  where: { categorie: { nom: "Électronique" } },
});

Modifier (Update)

Modifier un enregistrement
const produit = await prisma.produit.update({
  where: { id: 1 },
  data: { prix: 99.99 },
});

Supprimer (Delete)

Supprimer un enregistrement
await prisma.produit.delete({
  where: { id: 1 },
});

données de départ

Après un migrate reset, la base de données est vide. Utilisez des données de départ avec un seed.

Créer le fichier de seed

prisma/seed.ts
import { PrismaClient } from '../app/generated/prisma/client';
import { PrismaMariaDb } from '@prisma/adapter-mariadb';

const adapter = new PrismaMariaDb({
  url: process.env.DATABASE_URL,
});

const prisma = new PrismaClient({ adapter });

async function main() {
  // Créer les catégories
  const electronique = await prisma.categorie.create({
    data: { nom: 'Électronique' },
  });

  const accessoires = await prisma.categorie.create({
    data: { nom: 'Accessoires' },
  });

  // Créer les produits
  await prisma.produit.createMany({
    data: [
      {
        nom: 'Clavier mécanique',
        description: 'Clavier mécanique RGB',
        prix: 129.99,
        categorieId: electronique.id,
      },
      {
        nom: 'Souris ergonomique',
        description: 'Souris sans fil ergonomique',
        prix: 79.99,
        categorieId: accessoires.id,
      },
      {
        nom: 'Écran 27 pouces',
        description: 'Écran 4K IPS',
        prix: 449.99,
        categorieId: electronique.id,
      },
    ],
  });

  console.log('Données de seed insérées avec succès!');
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Configurer le script de seed

Ajoutez la configuration suivante dans votre prisma.config.ts :

prisma.config.ts
import "dotenv/config";
import { defineConfig } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
    seed: "tsx prisma/seed.ts",
  },
  datasource: {
    url: process.env["DATABASE_URL"],
  },
});

Exécuter le seed

console
npx prisma db seed