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 :
prisma
C'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/client
C'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 init
Cette 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/postgres
Ensuite 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 dev
Cette 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-lastname
Cette 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 lastname
firstname
par le champ name
name
Voici 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/client
Le @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 generate
Cette 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 :