Aller au contenu

API Routes (Route Handlers)

Qu'est-ce qu'une Route API

Les Route Handlers de Next.js permettent de créer des points d'accès API directement dans votre application. Pas besoin d'un serveur backend séparé.

Les routes API sont définies dans des fichiers route.ts à l'intérieur du dossier app/api/.

app/
└── api/
    └── produits/
        ├── route.ts          → /api/produits (GET, POST)
        └── [id]/
            └── route.ts      → /api/produits/1 (GET, PUT, DELETE)

Méthodes HTTP

Chaque fichier route.ts peut exporter des fonctions nommées selon la méthode HTTP : GET, POST, PUT, DELETE, etc.

GET et POST

app/api/produits/route.ts
import { prisma } from "@/lib/prisma";
import { NextResponse } from "next/server";

// GET /api/produits - Obtenir tous les produits
export async function GET() {
  const produits = await prisma.produit.findMany({
    include: { categorie: true },
  });
  return NextResponse.json(produits);
}

// POST /api/produits - Créer un produit
export async function POST(request: Request) {
  const body = await request.json();

  // Validation des données
  if (!body.nom || !body.prix || !body.categorieId) {
    return NextResponse.json(
      { erreur: "Les champs nom, prix et categorieId sont requis." },
      { status: 400 }
    );
  }

  const produit = await prisma.produit.create({
    data: {
      nom: body.nom,
      description: body.description,
      prix: body.prix,
      categorieId: body.categorieId,
    },
  });

  return NextResponse.json(produit, { status: 201 });
}

GET, PUT et DELETE avec paramètre dynamique

app/api/produits/[id]/route.ts
import { prisma } from "@/lib/prisma";
import { NextResponse } from "next/server";

interface RouteParams {
  params: Promise<{ id: string }>;
}

// GET /api/produits/:id - Obtenir un produit par son ID
export async function GET(request: Request, { params }: RouteParams) {
  const { id } = await params;
  const produit = await prisma.produit.findUnique({
    where: { id: Number(id) },
    include: { categorie: true },
  });

  if (!produit) {
    return NextResponse.json(
      { erreur: "Produit non trouvé" },
      { status: 404 }
    );
  }

  return NextResponse.json(produit);
}

// PUT /api/produits/:id - Modifier un produit
export async function PUT(request: Request, { params }: RouteParams) {
  const { id } = await params;
  const body = await request.json();

  const produit = await prisma.produit.update({
    where: { id: Number(id) },
    data: {
      nom: body.nom,
      description: body.description,
      prix: body.prix,
      categorieId: body.categorieId,
    },
  });

  return NextResponse.json(produit);
}

// DELETE /api/produits/:id - Supprimer un produit
export async function DELETE(request: Request, { params }: RouteParams) {
  const { id } = await params;

  await prisma.produit.delete({
    where: { id: Number(id) },
  });

  return NextResponse.json({ message: "Produit supprimé" });
}

Tester les routes API

Vous pouvez tester vos routes API avec un outil comme Thunder Client (extension VS Code) ou directement dans le navigateur pour les requêtes GET.

Exemple de requête GET

console
curl http://localhost:3000/api/produits

Exemple de requête POST

console
curl -X POST http://localhost:3000/api/produits \
  -H "Content-Type: application/json" \
  -d '{"nom": "Casque audio", "prix": 199.99, "categorieId": 1}'

Validation des données

Il est important de valider les données reçues avant de les traiter. Dans l'exemple ci-dessus, nous vérifions que les champs obligatoires sont présents :

Validation simple
if (!body.nom || !body.prix || !body.categorieId) {
  return NextResponse.json(
    { erreur: "Les champs nom, prix et categorieId sont requis." },
    { status: 400 }
  );
}

Pour des validations plus avancées, vous pouvez utiliser une bibliothèque comme Zod :

Validation avec Zod
import { z } from "zod";

const schemaProduit = z.object({
  nom: z.string().min(1, "Le nom est requis"),
  description: z.string().optional(),
  prix: z.number().positive("Le prix doit être positif"),
  categorieId: z.number().int("L'ID de catégorie doit être un entier"),
});

export async function POST(request: Request) {
  const body = await request.json();
  const resultat = schemaProduit.safeParse(body);

  if (!resultat.success) {
    return NextResponse.json(
      { erreurs: resultat.error.flatten().fieldErrors },
      { status: 400 }
    );
  }

  // Les données sont validées, on peut les utiliser en toute sécurité
  const produit = await prisma.produit.create({
    data: resultat.data,
  });

  return NextResponse.json(produit, { status: 201 });
}

Réponses JSON

La classe NextResponse fournit des méthodes utilitaires pour créer des réponses HTTP :

Différents types de réponses
import { NextResponse } from "next/server";

// Réponse avec données (200 par défaut)
return NextResponse.json({ nom: "Clavier" });

// Réponse avec code de statut personnalisé
return NextResponse.json(produit, { status: 201 });

// Réponse d'erreur
return NextResponse.json({ erreur: "Non trouvé" }, { status: 404 });