Cours Vibe coding avec l'IA · Module 14: Clone de Pinterest

API Routes - REST & GraphQL

1 min de lecture
Dans cette leçon2

Créer des points de terminaison d'API.

API REST

1// app/api/users/route.ts
2export async function GET() {
3  const users = await prisma.user.findMany();
4  return Response.json({ users });
5}
6
7export async function POST(request: Request) {
8  const body = await request.json();
9  const user = await prisma.user.create({ data: body });
10  return Response.json({ user }, { status: 201 });
11}

Routes dynamiques

1// app/api/users/[id]/route.ts
2export async function GET(
3  request: Request,
4  { params }: { params: { id: string } }
5) {
6  const user = await prisma.user.findUnique({
7    where: { id: parseInt(params.id) }
8  });
9  return Response.json({ user });
10}

À bientôt !

Code de cette leçon : App.tsx
1'use client';
2
3import { useState } from 'react';
4
5const relatedSearches = ['navire pirate', 'photographie de coucher de soleil', 'vagues de l\'océan', 'voile', 'design nautique'];
6
7export default function CameraSearch() {
8  const [imagePreview, setImagePreview] = useState<string | null>(null);
9  const [isSearching, setIsSearching] = useState(false);
10
11  const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
12    const file = e.target.files?.[0];
13    if (!file) return;
14
15    const reader = new FileReader();
16    reader.onload = (event) => {
17      setImagePreview(event.target?.result as string);
18      setIsSearching(true);
19      setTimeout(() => setIsSearching(false), 2000);
20    };
21    reader.readAsDataURL(file);
22  };
23
24  if (!imagePreview) {
25    return (
26      <div className="min-h-screen bg-white p-8">
27        <div className="max-w-4xl mx-auto text-center">
28          <h1 className="text-4xl font-bold mb-8">Rechercher par image</h1>
29
30          <label className="inline-block cursor-pointer">
31            <div className="w-64 h-64 border-4 border-dashed border-gray-300 rounded-2xl flex flex-col items-center justify-center hover:border-red-500 transition mx-auto">
32              <div className="text-6xl mb-4">◆</div>
33              <div className="font-semibold mb-2">Téléverser une image</div>
34              <div className="text-sm text-gray-600">ou glisser-déposer</div>
35            </div>
36            <input type="file" accept="image/*" onChange={handleImageUpload} className="hidden" />
37          </label>
38        </div>
39      </div>
40    );
41  }
42
43  return (
44    <div className="min-h-screen bg-white p-8">
45      <div className="max-w-6xl mx-auto">
46        {/* Image Preview */}
47        <div className="mb-8">
48          <img src={imagePreview} alt="Search query" className="max-w-sm mx-auto rounded-2xl shadow-lg" />
49        </div>
50
51        {/* Loading */}
52        {isSearching ? (
53          <div className="text-center text-gray-600 mb-8 text-lg">
54            Recherche de pins similaires...
55          </div>
56        ) : (
57          <>
58            {/* Related Searches */}
59            <div className="mb-8">
60              <h2 className="text-2xl font-bold mb-4">Recherches associées</h2>
61              <div className="flex flex-wrap gap-2">
62                {relatedSearches.map(search => (
63                  <button
64                    key={search}
65                    className="px-4 py-2 bg-gray-200 rounded-full hover:bg-gray-300 font-semibold"
66                  >
67                    {search}
68                  </button>
69                ))}
70              </div>
71            </div>
72
73            {/* Similar Pins */}
74            <div>
75              <h2 className="text-2xl font-bold mb-4">Pins similaires</h2>
76              <div className="grid grid-cols-4 gap-4">
77                {[1, 2, 3, 4, 5, 6, 7, 8].map(i => (
78                  <div key={i} className="aspect-[3/4] bg-gray-200 rounded-2xl" />
79                ))}
80              </div>
81            </div>
82          </>
83        )}
84      </div>
85    </div>
86  );
87}

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Comment crées-tu un endpoint qui répond à GET dans le App Router de Next.js ?

  2. 2. D'où le gestionnaire dans app/api/users/[id]/route.ts récupère-t-il l'id depuis l'URL ?

Tâches pratiques dans le jeu

  • Clique dans l'ordre

    Clique sur les éléments pour construire le retour d'une liste d'utilisateurs en JSON :

  • Tri horizontal

    Ordonne les éléments d'une ligne qui renvoie l'enregistrement créé avec le statut 201 :

  • Éditeur de code

    Crée des endpoints d'API REST

Articles utiles