Cours Vibe coding avec l'IA · Module 14: Clone de Pinterest
API Routes - REST & GraphQL
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. Comment crées-tu un endpoint qui répond à GET dans le App Router de Next.js ?
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