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¶
Initialiser Prisma avec MySQL¶
On se retrouve avec :
- Un dossier
prisma/contenant un fichierschema.prisma - Un fichier
.envavec la variableDATABASE_URL
Configurer la connexion à la base de données¶
Modifiez le fichier .env avec vos informations de connexion MySQL :
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.
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 :
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¶
En arrière-plan, Prisma :
- Compare le schéma avec l'état actuel de la base de données
- Génère le fichier SQL correspondant à la différence
- Exécute ce SQL sur la base de données
- 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 :
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 :
Configuration du client¶
Voici le code pour créer une instance de prisma en singleton, utile pour les fonctions plus bas.
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)¶
const produit = await prisma.produit.create({
data: {
nom: "Clavier mécanique",
description: "Clavier RGB",
prix: 129.99,
categorieId: 1,
},
});
Lire (Read)¶
// 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)¶
const produit = await prisma.produit.update({
where: { id: 1 },
data: { prix: 99.99 },
});
Supprimer (Delete)¶
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¶
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 :
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"],
},
});