Introduction
Gérer les avis clients est crucial pour la confiance et le SEO. En 2026, les systèmes doivent inclure validation stricte, modération automatisée et affichage performant. Ce tutoriel vous guide pas à pas dans la création d'une solution complète avec Next.js App Router, Prisma et TypeScript. Vous apprendrez à sécuriser les soumissions, calculer les notes moyennes et optimiser les requêtes pour des milliers d'avis.
Prérequis
- Next.js 15 avec TypeScript
- Node.js 20+
- Prisma 5.20+
- Base de données PostgreSQL
- Connaissances solides en React et API routes
Schéma Prisma pour les avis
model Review {
id Int @id @default(autoincrement())
rating Int
comment String @db.Text
approved Boolean @default(false)
userId String
productId String
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
product Product @relation(fields: [productId], references: [id])
}Ce schéma définit le modèle Review avec validation de note, modération et relations. Le champ approved permet la modération manuelle avant affichage public.
Route API POST pour soumettre un avis
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { z } from 'zod';
const reviewSchema = z.object({
rating: z.number().min(1).max(5),
comment: z.string().min(10).max(500),
productId: z.string(),
});
export async function POST(req: NextRequest) {
const body = await req.json();
const parsed = reviewSchema.safeParse(body);
if (!parsed.success) return NextResponse.json({ error: 'Invalid data' }, { status: 400 });
const { rating, comment, productId } = parsed.data;
const review = await prisma.review.create({
data: { rating, comment, productId, userId: 'user-123', approved: false }
});
return NextResponse.json(review, { status: 201 });
}Cette route utilise Zod pour valider les données entrantes et crée l'avis en mode non-approuvé par défaut. Elle empêche les soumissions invalides et les injections.
Route API GET pour les avis approuvés
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const productId = searchParams.get('productId');
if (!productId) return NextResponse.json({ error: 'Missing productId' }, { status: 400 });
const reviews = await prisma.review.findMany({
where: { productId, approved: true },
orderBy: { createdAt: 'desc' },
take: 20
});
return NextResponse.json(reviews);
}Cette requête optimise les performances en filtrant uniquement les avis approuvés et en limitant les résultats. Idéale pour l'affichage côté client.
Composant d'affichage des étoiles
import React from 'react';
interface Props { rating: number; }
export default function ReviewStars({ rating }: Props) {
return (
<div className="flex">
{Array.from({ length: 5 }).map((_, i) => (
<span key={i} className={i < rating ? 'text-yellow-500' : 'text-gray-300'}>★</span>
))}
</div>
);
}Composant réutilisable et accessible qui affiche visuellement la note. Il est optimisé pour le rendu côté serveur et évite les re-rendus inutiles.
Fonction de calcul de note moyenne
import { prisma } from './prisma';
export async function getAverageRating(productId: string): Promise<number> {
const result = await prisma.review.aggregate({
where: { productId, approved: true },
_avg: { rating: true }
});
return result._avg.rating || 0;
}Utilise l'agrégation Prisma pour calculer la moyenne en base de données, évitant de charger tous les avis en mémoire. Très performant à grande échelle.
Bonnes pratiques
- Toujours valider les entrées côté serveur avec Zod
- Modérer les avis avant publication
- Utiliser des index sur productId et approved
- Limiter le nombre d'avis retournés par requête
- Protéger les routes avec authentification
Erreurs courantes à éviter
- Oublier la validation et autoriser les notes invalides
- Ne pas séparer création et modération
- Charger tous les avis sans pagination
- Ignorer les injections SQL via des requêtes dynamiques mal construites
Pour aller plus loin
Découvrez nos formations avancées sur Next.js et les systèmes e-commerce.