Next.js et Prisma : créer une app fullstack
Publié le 22/07/2024 - Ecrit par Antoine Bourin
6 minutes
Prisma est un ORM open-source. Celui-ci permet de simplifier les requêtes vers vos bases de données : migrations, schémas, souscriptions...
Next.js vous offre un contexte de rendu serveur (composants serveurs, API route handler) qui vous permet d'échanger directement avec des ressources back-end, comme Prisma.
Nous allons construire une application complète avec Next JS pour la création de l'interface, l'utilisation de server actions et Prisma pour échanger avec notre base de données : insérer et afficher des données.
Mise en place de l'application
Commençons par l'installation de la stack technique de l'application. Next.js, framework React pour la création de notre application web. Prisma permettra d'interfacer notre application avec une base de données.
Installation de Next.js
Pour l'installation de Next.js, vous pouvez utiliser create-next-app
. Il s'agit d'un CLI proposé par le framework pour installer l'application avec une configuration minimale, prête à être modifiée.
Exécutez la commande suivante pour créer l'application :
npx create-next-app@latest
Vous serez amené à répondre à un ensemble de questions pour déterminer la configuration que vous souhaitez. Vous pouvez laisser les choix par défaut.
Une fois la nouvelle application installée, accédez au dossier de l'application et lancez le serveur de développement avec la commande suivante :
npm run dev
Vous devriez pouvoir constater le lancement de votre application en accédant à l'URL http://localhost:3000
:
Installation de Prisma
Pour commencer à utiliser Prisma dans notre application Next.js, il nous faudra installer le CLI Prisma pour pouvoir par la suite initialiser la librairie et exécuter des migrations. Exécutez la commande la suivante :
npm install prisma --save-dev
Schéma de données et initialisation de Prisma
Maintenant que Prisma est installé, il nous faut l'initialiser dans notre application.
La commande suivante permet d'initialiser la librairie avec une base de données SQLite.
Nous utiliserons ici SQLite car il s'agit d'un type de base de données léger et embarqué facile à mettre en place. Sachez que Prisma supporte également tout type de base de données comme MySQL
, MongoDB
, PostgreSQL
...
Voir tous les types de base de données disponibles.
npx prisma init --datasource-provider sqlite
Une fois cette commande effectuée, vous constaterez qu'un nouveau répertoire prisma
est apparu. A l'intérieur de celui-ci, un fichier schema.prisma
. C'est ce fichier que nous modifierons pour construire le schéma de données de notre base.
Pour cet exemple, nous mettrons en place un schéma simple de deux entités : Note et Category. Ouvrez le fichier schema.prisma
et éditez le comme suit :
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Category {
id Int @id @default(autoincrement())
name String
notes Note[]
}
model Note {
id Int @id @default(autoincrement())
title String
category Category @relation(fields: [categoryId], references: [id])
categoryId Int
}
Exécution des migrations
Une fois le schéma modifié, nous devons exécuter une commande de migration.
npx prisma migrate dev --name init
Cette migration permettra de :
- Créer la base de données SQLite, en local, sous forme de fichier
dev.db
- Créer le fichier de migration dans le répertoire prisma et exécuter la migration SQL dans la base.
- Exécute
prisma generate
, qui elle-même installera le paquet@prisma/client
. Ce paquet permettra d'utiliser PrismaClient avec le modèle de base de données déjà appliqué.
Votre base de données est prête !
Utilisation de PrismaClient en singleton
@prisma/client
est la librairie de Prisma qui vous permettra par la suite d'interagir avec votre base de données.
Spécifiquement pour Next.js, pour éviter tous problèmes en mode de développement, nous devons créer un service pour récupérer une seule instance de cette classe (singleton). Créez le fichier prisma.ts
où vous souhaitez dans l'application et insérez le code suivant :
import { PrismaClient } from "@prisma/client";
const prismaClientSingleton = () => {
return new PrismaClient();
};
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();
export default prisma;
if (process.env.NODE_ENV !== "production") globalThis.prismaGlobal = prisma;
Visualisation de la base de données avec Prisma Studio
Vérifiez si votre base de données a bien été initialisée avec Prisma studio en effectuant la commande suivante :
npx prisma studio
Vous pourrez accéder à l'interface grâce à l'URL http://localhost:5555
. Vous pouvez également ajouter de nouvelles données depuis l'interface.
Interagir avec une base de données avec Next.js et Prisma
Maintenant que tout est prêt pour notre base de données, il est temps d'interagir avec grâce à notre application front-end.
Création de l'interface
Notre premier objectif sera d'ajouter deux formulaires, l'un pour la création d'une catégorie, l'autre pour l'ajout d'une nouvelle note.
Commençons par créer notre formulaire d'ajout de catégorie, CategoryForm
:
const CategoryForm = () => {
return (
<form>
<input type="text" name="category-title" placeholder="Catégorie..." />
<input type="submit" value="Enregistrer" />
</form>
);
};
export default CategoryForm;
Celui-ci peut être importé dans notre page d'accueil, page.tsx
:
import CategoryForm from "@/components/CategoryForm";
import styles from "./page.module.css";
export default function Home() {
return (
<main className={styles.main}>
<CategoryForm />
</main>
);
}
Action serveur et requête Prisma
Il ne nous manque plus qu'à créer l'action serveur (server action) qui permettra de créer la nouvelle catégorie dans la base de données grâce à Prisma :
"use server";
import prisma from "@/prisma";
export const createCategory = async (formData: FormData) => {
const categoryTitle = formData.get("category-title") as string;
await prisma.category.create({
data: {
name: categoryTitle,
},
});
};
Remarquez que cette action reçoit en paramètre les valeurs des champs sous l'objet FormData
.
Il ne nous manque plus qu'à modifier le formulaire pour lier l'action dans l'attribut action
de la balise form
:
import { createCategory } from "@/actions/category";
const CategoryForm = () => {
return (
<form action={createCategory}>
<input type="text" name="category-title" placeholder="Catégorie..." />
<input type="submit" value="Enregistrer" />
</form>
);
};
export default CategoryForm;
Essayez maintenant de créer une nouvelle catégorie sur l'interface. Elle est bien ajoutée en base de données !
Réinitialisation du formulaire
Pour améliorer notre formulaire, je vous propose de reset (vider) les champs de celui-ci lorsqu'un nouvel ajout est effectué.
Pour ce faire, on peut se servir de la référence du composant avec le hook useRef
et utiliser la fonction reset
:
"use client";
import { createCategory } from "@/actions/category";
import { useRef } from "react";
const CategoryForm = () => {
const formRef = useRef<HTMLFormElement>(null);
return (
<form
ref={formRef}
action={async (formData) => {
await createCategory(formData);
formRef.current?.reset();
}}
>
<input type="text" name="category-title" placeholder="Catégorie..." />
<input type="submit" value="Enregistrer" />
</form>
);
};
export default CategoryForm;
Le fait d'utiliser le hook useRef
requiert que notre composant devienne composant client et donc que la directive "use client"
soit ajoutée.
Affichage des données et mise en cache
Maintenant que nos catégories sont stockées en base de données, place à la récupération et à l'affichage.
La grande force de Next.js réside également dans le fait que vous puissiez choisir, précisément, la politique de cache de vos données.
Récupération des données
Pour récupérer les données des catégories et les afficher sur notre page, rien de plus simple.
Un composant Next.js est par défaut un composant serveur. Cela signifie que celui-ci peut faire appel à des ressources back-end et donc, faire appel à Prisma :
import CategoryForm from "@/components/CategoryForm";
import styles from "./page.module.css";
import prisma from "@/prisma";
export default async function Home() {
const categories = await prisma.category.findMany();
return (
<main className={styles.main}>
<CategoryForm />
<h2>Catégories</h2>
<ul>
{categories.map((category) => (
<li key={category.id}>{category.name}</li>
))}
</ul>
</main>
);
}
A noter que vous devez rajouter le mot clé async
pour votre composant pour qu'il puisse gérer des appels asynchrones comme celui fait vers notre base de données.
Gestion du cache : revalidatePath
Nos catégories sont maintenant bien affichées.
Cependant, vous constaterez qu'en ajoutant une nouvelle catégorie, les données de la page resteront les mêmes. Même en rafraîchissant la page.
Cela est dû au fait qu'une page avec Next est par défaut statique. Les données de celle-ci sont récupérées une seule fois et sont gardées en cache jusqu'à ce qu'elles soient invalidées, ou purgées.
Pour rafraîchir les données affichées lorsqu'une nouvelle catégorie est ajoutée, il nous est possible d'utiliser la fonction revalidatePath
.
Modifions donc l'action serveur avec l'appel de cette fonction :
"use server";
import prisma from "@/prisma";
import { revalidatePath } from "next/cache";
export const createCategory = async (formData: FormData) => {
const categoryTitle = formData.get("category-title") as string;
await prisma.category.create({
data: {
name: categoryTitle,
},
});
revalidatePath("/");
};
Désormais, une fois qu'une catégorie est ajoutée, le cache affecté à la page d'accueil est invalidé.
Réessayez d'ajouter une catégorie. Tada ! La nouvelle catégorie s'affiche immédiatement.
Avez-vous trouvé ce contenu utile ?
Envie d'en apprendre plus sur Next.js ?
Découvrez une formation Next.js complète :
- Comprendre et créer un système de routage complet
- Créer des routes statiques et dynamique
- Maîtriser le rendu serveur et client
- Utiliser tous les atouts majeurs proposés par le framework
Plus de 35 heures de contenu vidéo
Plus de 80 cours écrits