Configuration de Prisma pour un e-commerce React Router 7
Découvre comment installer, configurer Prisma et modéliser ta base pour un projet e-commerce React Router. Boost ton projet avec un schéma clair et un seed rapide.
Pourquoi utiliser Prisma pour notre boutique ?
Le modèle e-commerce que nous allons bâtir est loin du simple « todo ». On aura des produits, des catégories, un panier, des commandes, des factures Stripe... bref : beaucoup de relations. Prisma nous offre :
- un schéma déclaratif lisible (et versionné) ;
- un client TypeScript auto-généré avec autocomplétion solide ;
- un service PostgreSQL serverless gratuit (500 Mo + 100 000 req/mois) parfait pour le dev.
Dans cette leçon on va :
- installer Prisma et la base serverless fournie par Prisma Cloud ;
- modéliser la première version de notre schéma e-commerce ;
- créer la migration, le client et un script
seed.tspour remplir 10 produits ; - corriger les petits pièges vus dans la vidéo (Decimal, import du client, Query Compiler).
Installer Prisma + base PostgreSQL en 1 commande
1npx prisma@latest init --db
- Le CLI te demande d’ouvrir un lien ; connecte-toi avec GitHub.
- Choisis la région
eu-west-par (Paris)– latence minimale pour tes visiteurs français. - Nom du projet :
shop-router.
Le CLI ajoute deux fichiers :
1prisma/schema.prisma # schéma vide2.env # DATABASE_URL=https://…cloud.prisma.sh
La Database_URL pointe déjà vers ta base cloud PostgreSQL – pas de Docker local à configurer !
Installer les dépendances runtime & dev
1npm install prisma @prisma/client @prisma/extension-accelerate
prisma: CLI + générateur ;@prisma/client: ORM typé côté serveur ;@prisma/extension-accelerate: couche cache ✅.
Déclarer le client singleton
1import { PrismaClient } from "@prisma/client";2import { acceleratePrisma } from "@prisma/extension-accelerate";34declare global {5var prisma: ReturnType<typeof getPrisma> | undefined;6}78function getPrisma() {9return new PrismaClient().$extends(acceleratePrisma());10}1112export const prisma = globalThis.prisma ??= getPrisma();
- Singleton ➜ HMR ne crée qu’une connexion.
- Extension Accelerate ➜ cache partagé (tu pourras l’activer route par route plus tard).
Concevoir le schéma e-commerce
Plutôt que tout taper à la main on part du prompt résumé ci-dessous ; copie-le dans ChatGPT/Cursor :
1// E-commerce SaaS (non multi-tenant)2// User, Category, Product (+images), Cart, Order, Invoice, EmailLog3// Stripe product & price ids, guest checkout, stock management…
L’IA génère un brouillon Prisma que tu colles dans prisma/schema.prisma, puis relis chaque modèle :
Extraits clés
1model Product {2id String @id @default(uuid())3name String4slug String @unique5description String?6currency String @default("EUR")7price Decimal @db.Decimal(10, 2) // ^? centimes ? voir ci-dessous8stock Int @default(0)9isActive Boolean @default(true)10stripeProductId String? @unique11stripePriceId String? @unique12images Image[]13categories Category[]14CartItem CartItem[]15createdAt DateTime @default(now())16updatedAt DateTime @updatedAt17}
Warning
Si tu préfères, change
Decimal en Int + commentaire : « valeur en centimes ».Nous garderons Decimal 2 décimales pour l’instant et convertirons côté UI.
Générer la migration et le client
1npx prisma migrate dev --name init-shop-router
- Prisma pousse le SQL dans ta base cloud.
- Le client TypeScript est généré dans
generated/prisma(grâce à l’optionoutput).
Note
export is not defined, installe la preview feature Query Compiler dans le générateur :1generator client {2provider = "prisma-client"3output = "../generated/prisma"4previewFeatures = ["queryCompiler", "relationJoins", "driverAdapters"]5moduleFormat = "esm"6}
Regénère :
1npx prisma generate
Script de seed : 3 catégories, 10 produits aléatoires
1import { prisma } from "~/server/db.server";23const slug = (s: string) =>4s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");56async function main() {7await prisma.product.deleteMany();8await prisma.category.deleteMany();910const cats = await prisma.$transaction(11["Vêtements", "Accessoires", "Maison"].map((n) =>12prisma.category.create({ data: { name: n, slug: slug(n) } }),13),14);1516for (let i = 0; i < 10; i++) {17const name = `Produit ${i + 1}`;18await prisma.product.create({19data: {20name,21slug: `${slug(name)}-${i}`,22description: `Description de ${name}`,23price: (Math.random() * 150 + 5).toFixed(2),24stock: Math.floor(Math.random() * 100),25categories: { connect: { id: cats[i % cats.length].id } },26},27});28}29}3031main().finally(() => prisma.$disconnect());
Exécution :
1npm run dev # lance l'app dans un 1er terminal2npx tsx prisma/seed.ts
Prisma Studio (npx prisma studio) montre tes 10 produits et 3 catégories.
Afficher les produits côté Home
1import { prisma } from "~/server/db.server";2import { useLoaderData } from "react-router";34export async function loader() {5return prisma.product.findMany({6select: { id: true, name: true, description: true, price: true },7});8}910export default function Home() {11const products = useLoaderData<typeof loader>();12return (13<main className="grid gap-6 p-8 md:grid-cols-2 lg:grid-cols-3">14{products.map((p) => (15<article key={p.id} className="rounded-lg border p-4 shadow">16<h2 className="text-lg font-semibold">{p.name}</h2>17<p className="text-sm opacity-70">{p.description}</p>18<p className="mt-2 text-right text-sky-600 font-bold">19{Number(p.price).toFixed(2)} €20</p>21</article>22))}23</main>24);25}
L’écran d’accueil affiche désormais 10 cartes produits — le back-end est prêt !
Pièges & correctifs vus dans la vidéo
| Problème | Solution |
|---|---|
export is not defined au runtime | Ajoute queryCompiler + moduleFormat = "esm" dans le générateur. |
Import ancien chemin .prisma/client | Utilise toujours @prisma/client généré dans generated/prisma. |
Decimal affiché [Object object] | Convertis via Number(p.price) ou p.price.toFixed(2) côté UI. |
Ce que tu dois committer
1git checkout -b 7-2-configuration-de-prisma2git add .3git commit -m "feat: configure Prisma + schema e-commerce + seed"4git push -u origin 7-2-configuration-de-prisma
Pull-request, merge, et ta base est officiellement dans le dépôt. 🚀
Important
Comprendre les concepts fondamentaux
Quelle est la principale différence entre les composants client et serveur dans React ?
Optimisation des performances
Quelle technique est recommandée pour éviter les rendus inutiles dans React ?
Architecture des données
Quel hook permet de gérer les effets de bord dans un composant React ?