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

Base de données - Prisma ORM

1 min de lecture
Dans cette leçon3

Intégration de la base de données.

Configuration de Prisma

1npm install prisma @prisma/client
2npx prisma init

Schéma

1// prisma/schema.prisma
2model User {
3  id    Int     @id @default(autoincrement())
4  name  String
5  email String  @unique
6  posts Post[]
7}
8
9model Post {
10  id       Int    @id @default(autoincrement())
11  title    String
12  content  String
13  author   User   @relation(fields: [authorId], references: [id])
14  authorId Int
15}

Utilisation

1import { PrismaClient } from '@prisma/client';
2
3const prisma = new PrismaClient();
4
5// Create
6await prisma.user.create({
7  data: {
8    name: 'Jack',
9    email: 'jack@pirates.com'
10  }
11});
12
13// Read
14const users = await prisma.user.findMany({
15  include: { posts: true }
16});

À bientôt !

Code de cette leçon : App.tsx
1'use client';
2
3import { useState } from 'react';
4
5interface Board {
6  id: string;
7  name: string;
8  pinCount: number;
9  coverImage?: string;
10}
11
12const mockBoards: Board[] = [
13  { id: '1', name: 'Idées de voyage', pinCount: 247, coverImage: 'https://images.unsplash.com/photo-1488646953014-85cb44e25828?w=100&h=100&fit=crop' },
14  { id: '2', name: 'Recettes', pinCount: 156, coverImage: 'https://images.unsplash.com/photo-1495521821757-a1efb6729352?w=100&h=100&fit=crop' },
15  { id: '3', name: 'Décoration intérieure', pinCount: 89, coverImage: 'https://images.unsplash.com/photo-1616486338812-3dadae4b4ace?w=100&h=100&fit=crop' }
16];
17
18const mockPin = {
19  title: 'Magnifique coucher de soleil',
20  description: 'Superbe coucher de soleil sur l\'océan avec la lumière de l\'heure dorée',
21  imageUrl: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=100&h=100&fit=crop'
22};
23
24export default function SavePinModal() {
25  const [isOpen, setIsOpen] = useState(true);
26  const [selectedBoardId, setSelectedBoardId] = useState<string | null>(null);
27
28  if (!isOpen) {
29    return (
30      <div className="min-h-screen bg-gray-100 flex items-center justify-center">
31        <button
32          onClick={() => setIsOpen(true)}
33          className="px-6 py-3 bg-red-600 text-white rounded-full font-semibold"
34        >
35          Enregistrer le pin
36        </button>
37      </div>
38    );
39  }
40
41  return (
42    <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
43      <div className="bg-white rounded-2xl max-w-md w-full max-h-[80vh] flex flex-col">
44        {/* Header */}
45        <div className="p-6 border-b border-gray-200">
46          <div className="flex items-center justify-between">
47            <h2 className="text-xl font-bold">Enregistrer dans un tableau</h2>
48            <button onClick={() => setIsOpen(false)} className="text-gray-500 hover:text-black text-xl">
49              ×
50            </button>
51          </div>
52        </div>
53
54        {/* Pin Preview */}
55        <div className="p-6 border-b border-gray-200">
56          <div className="flex gap-3">
57            <img src={mockPin.imageUrl} alt={mockPin.title} className="w-16 h-16 rounded object-cover" />
58            <div className="flex-1">
59              <h3 className="font-semibold">{mockPin.title}</h3>
60              <p className="text-sm text-gray-600 line-clamp-2">{mockPin.description}</p>
61            </div>
62          </div>
63        </div>
64
65        {/* Boards */}
66        <div className="flex-1 overflow-y-auto p-6">
67          <button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-2xl hover:border-gray-400 mb-4">
68            <div className="flex items-center gap-3">
69              <div className="w-12 h-12 bg-gray-200 rounded flex items-center justify-center text-2xl">
70                +
71              </div>
72              <span className="font-semibold">Créer un tableau</span>
73            </div>
74          </button>
75
76          {mockBoards.map(board => (
77            <button
78              key={board.id}
79              onClick={() => setSelectedBoardId(board.id)}
80              className={`w-full p-4 rounded-2xl hover:bg-gray-100 mb-2 ${
81                selectedBoardId === board.id ? 'bg-gray-100 ring-2 ring-blue-500' : ''
82              }`}
83            >
84              <div className="flex items-center gap-3">
85                {board.coverImage ? (
86                  <img src={board.coverImage} alt={board.name} className="w-12 h-12 rounded object-cover" />
87                ) : (
88                  <div className="w-12 h-12 bg-gray-200 rounded" />
89                )}
90                <div className="flex-1 text-left">
91                  <div className="font-semibold">{board.name}</div>
92                  <div className="text-sm text-gray-600">{board.pinCount} pins</div>
93                </div>
94                {selectedBoardId === board.id && <div className="text-blue-500 text-xl">√</div>}
95              </div>
96            </button>
97          ))}
98        </div>
99
100        {/* Save Button */}
101        <div className="p-6 border-t border-gray-200">
102          <button
103            disabled={!selectedBoardId}
104            className="w-full py-3 bg-red-600 text-white rounded-full font-semibold hover:bg-red-700 disabled:bg-gray-300"
105          >
106            Enregistrer
107          </button>
108        </div>
109      </div>
110    </div>
111  );
112}

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. Qu'est-ce que Prisma dans une application ?

  2. 2. Comment récupères-tu des enregistrements liés dans findMany (par exemple les posts d'un utilisateur) ?

Tâches pratiques dans le jeu

  • Clique dans l'ordre

    Clique sur les éléments pour construire la commande d'installation de Prisma :

  • Éditeur de code

    Crée un modèle et des requêtes Prisma

  • Tri vertical

    Ordonne les étapes du travail avec la base de données via Prisma :

Articles utiles