Guide complet de Prisma avec NextJS
09/02/2025 • Melvynx
Je vais t'expliquer comment utiliser Prisma dans une application JavaScript en prenant notamment l'exemple de NextJS.
Ce guide va tout t'expliquer :
Prisma vient avec deux librairies :
prismaC'est la librairie principale de Prisma qui va être installée dans les dépendances de dev. Elle permet principalement d'exécuter des commandes dans le terminal pour faire des actions.
@prisma/clientC'est la librairie qui va être dans les dépendances et c'est elle qui va permettre de faire des requêtes à la base de données.
Pour installer Prisma, il y a une commande qu'on peut exécuter :
npx prisma initCette commande va installer les dépendances nécessaires et rajouter un dossier prisma dans ton projet avec un fichier schema.prisma.
Ce fichier schema.prisma va nous permettre de définir les tables de notre base de données.
On va partir sur le principe que tu utilises PostgreSQL comme base de données, tu vas devoir modifier le fichier schema.prisma pour ajouter ta DATABASE_URL dans le fichier .env.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}Et dans ton fichier .env, tu vas ajouter :
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgresEnsuite tu peux ajouter tes tables et relations. Voici l'exemple de la documentation :
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String @db.VarChar(255)
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model Profile {
id Int @id @default(autoincrement())
bio String?
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
}Ici tu peux voir qu'on a des relations, en installant l'extension VSCode Prisma tu verras qu'il t'aide super bien à créer des schémas cohérents.
Une fois qu'on a ajouté ça, on doit créer une migration.
Le système de migration est un système populaire dans les bases de données qui permet de venir modifier la structure de ta base de données de manière évolutive.
C'est-à-dire que chaque fois qu'on va modifier le schéma, on va créer une migration, cette migration va venir modifier la base de données et potentiellement les données.
Ensuite quand on vient lancer la migration, celle-ci va appliquer chaque migration dans l'ordre, ce qui permet de faire des migrations sans risquer de perdre des données.
Pour créer une migration, on peut utiliser la commande suivante :
npx prisma migrate devCette commande va créer une migration et l'appliquer à la base de données. Tu verras qu'il va te demander un nom pour la migration, tu peux simplement inscrire "init database" ou "initial migration".
Imagine qu'on souhaite remplacer name par firstname et lastname dans notre schéma. Dans ce cas-là, on va modifier
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
firstname String?
lastname String?
posts Post[]
profile Profile?
}Dans ce cas-là c'est compliqué car on va perdre des données, car si on fait une migration qui supprime le champ name et rajoute firstname et lastname, on va perdre toutes les données de name.
Pour éviter ça, on va créer une nouvelle migration qui va venir ajouter les champs firstname et lastname et les remplir avec les données de name.
npx prisma migrate --create-only --name add-firstname-lastnameCette commande va créer un fichier de migration dans ton projet, tu peux le voir dans le dossier prisma/migrations.
Ensuite on va pouvoir faire du SQL :
firstname et lastnamefirstname par le champ namenameVoici le code SQL :
ALTER TABLE "User" ADD COLUMN "firstname" text;
ALTER TABLE "User" ADD COLUMN "lastname" text;
UPDATE "User" SET "firstname" = "name";
ALTER TABLE "User" DROP COLUMN "name";Et de cette manière, quand on va déployer notre site sur notre database de production, la migration va se faire et on va éviter de perdre des données.
Cette migration va ensuite s'effectuer chaque fois qu'on va lancer les migrations sur une "nouvelle base de données".
@prisma/clientLe @prisma/client est généré en fonction de ton schéma. C'est-à-dire que tous les types sont générés chaque fois que tu fais une migration. Tu peux aussi générer les types avec la commande suivante :
npx prisma generateCette commande va venir générer le @prisma/client et les types dans le dossier node_modules.
C'est comme ça qu'ensuite tu vas pouvoir faire des requêtes à la base de données de manière "type-safe".
Pour ajouter Prisma, on va utiliser un Singleton expliqué dans la documentation.
Dans un fichier lib/prisma.ts on va instancier notre Prisma :
// lib/prisma.ts
import { PrismaClient } from "~/generated/client";
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;Ce code permet d'éviter d'instancier plusieurs fois le PrismaClient dans ton application. Notamment quand tu publies l'application sur Vercel en "Serverless".
Une fois fait, tu peux utiliser prisma à n'importe quel endroit backend de ton application, comme un API Routes qui viendrait retourner tous les posts dans app/api/posts/route.ts :
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
export async function GET(request: Request) {
const posts = await prisma.post.findMany();
return NextResponse.json(posts);
}Ce code vient récupérer tous les posts disponibles et vient les retourner en format JSON. Tu peux le voir ici, mais avec Prisma tout est type-safe c'est-à-dire que quand tu écris prisma. tu as une auto-complétion automatique sur VSCode qui te propose post.
Tu peux utiliser Prisma pour faire à peu près tout ce qui est possible de faire avec SQL sauf si tu vas dans des cas trop compliqués. Mais voici des exemples typiques.
Récupérer tous les posts qui commencent par "Pourquoi"
const posts = await prisma.post.findMany({
where: {
title: {
startsWith: "Pourquoi",
},
},
});where permet de filtrer les donnéesRécupérer l'user avec l'id 1 et tous ses posts
const user = await prisma.user.findUnique({
where: {
id: 1,
},
include: {
posts: true,
},
});
const posts = user.posts;include vient inclure des relations à la valeur de retourRécupérer l'user le nom et le titre de tous les posts de l'utilisateur avec l'id 1
const posts = await prisma.post.findMany({
where: {
authorId: 1,
},
select: {
title: true,
author: {
select: { name: true },
},
},
});select permet de sélectionner et récupérer uniquement les champs dont tu as besoin. Tu peux aussi sélectionner des données dans les relations.Récupérer le nombre de posts de l'utilisateur avec l'id 1
const posts = await prisma.post.count({
where: {
authorId: 1,
},
});count permet de récupérer le nombre de donnéesRécupérer les 10 derniers posts
const posts = await prisma.post.findMany({
take: 10,
orderBy: {
createdAt: "desc",
},
});take permet de récupérer un nombre de donnéesorderBy permet de trier les donnéesIl existe évidemment plein d'autres usages et de choses à faire. Je t'invite à utiliser ChatGPT si tu as des questions, il répond vraiment bien. Tu peux aussi lire la documentation.
Un truc que j'aime vraiment faire avec Prisma, c'est de générer des types TypeScript en fonction des query que je fais. Souvent dans mes applications je crée des méthodes pour séparer mes query de mes API Routes par exemple. Par exemple, je vais récupérer le user et ses posts dans une méthode :
export const getUserWithPosts = async (userId: number) => {
const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
name: true,
posts: {
select: {
id: true,
title: true,
},
},
},
});
return user;
};Et ensuite souvent j'ai envie d'avoir un type qui correspond à cet objet. Pour ça, Prisma a prévu le coup et tu peux faire ceci.
import { Prisma } from "~/generated/client";
type UserWithPosts = Prisma.PromiseReturnType<typeof getUserWithPosts>;
// UserWithPosts = {
// id: number;
// name: string;
// posts: { id: number; title: string }[];
// }Et là, tu as un type qui correspond à l'objet que tu retournes dans ta fonction.
Tu peux donc créer un Server Component qui va prendre en props ce fameux user avec le bon type :
export const UserWithPostsCard = ({ user }: { user: UserWithPosts }) => {
return (
<div>
<h1>{user.name}</h1>
{user.posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
};Et là, tu as une auto-complétion sur VSCode qui te propose les champs de l'objet.
L'avantage de dingue ici c'est que si tu viens modifier les données que tu récupères dans la méthode getUserWithPosts tu vas avoir TypeScript qui va automatiquement modifier le type et t'afficher ou non des erreurs en fonction. J'utilise ça tout le temps et c'est vraiment très puissant.
Dans ce guide je t'ai présenté les points les plus importants de Prisma ! J'ai une formation "NextReact" qui contient un module entier sur Prisma et tu peux tester ma formation gratuitement ici :