Vibe coding with AI course · Module 14: Pinterest Clone
Database - Prisma ORM
In this lesson3
Database integration.
Prisma Setup
npm install prisma @prisma/client
npx prisma initSchema
// prisma/schema.prisma
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String
author User @relation(fields: [authorId], references: [id])
authorId Int
}Usage
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Create
await prisma.user.create({
data: {
name: 'Jack',
email: 'jack@pirates.com'
}
});
// Read
const users = await prisma.user.findMany({
include: { posts: true }
});See you there!
Code for this lesson: App.tsx
'use client';
import { useState } from 'react';
interface Board {
id: string;
name: string;
pinCount: number;
coverImage?: string;
}
const mockBoards: Board[] = [
{ id: '1', name: 'Travel Ideas', pinCount: 247, coverImage: 'https://images.unsplash.com/photo-1488646953014-85cb44e25828?w=100&h=100&fit=crop' },
{ id: '2', name: 'Recipes', pinCount: 156, coverImage: 'https://images.unsplash.com/photo-1495521821757-a1efb6729352?w=100&h=100&fit=crop' },
{ id: '3', name: 'Home Decor', pinCount: 89, coverImage: 'https://images.unsplash.com/photo-1616486338812-3dadae4b4ace?w=100&h=100&fit=crop' }
];
const mockPin = {
title: 'Beautiful Sunset',
description: 'Amazing sunset over the ocean with golden hour lighting',
imageUrl: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=100&h=100&fit=crop'
};
export default function SavePinModal() {
const [isOpen, setIsOpen] = useState(true);
const [selectedBoardId, setSelectedBoardId] = useState<string | null>(null);
if (!isOpen) {
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<button
onClick={() => setIsOpen(true)}
className="px-6 py-3 bg-red-600 text-white rounded-full font-semibold"
>
Save Pin
</button>
</div>
);
}
return (
<div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div className="bg-white rounded-2xl max-w-md w-full max-h-[80vh] flex flex-col">
{/* Header */}
<div className="p-6 border-b border-gray-200">
<div className="flex items-center justify-between">
<h2 className="text-xl font-bold">Save to board</h2>
<button onClick={() => setIsOpen(false)} className="text-gray-500 hover:text-black text-xl">
×
</button>
</div>
</div>
{/* Pin Preview */}
<div className="p-6 border-b border-gray-200">
<div className="flex gap-3">
<img src={mockPin.imageUrl} alt={mockPin.title} className="w-16 h-16 rounded object-cover" />
<div className="flex-1">
<h3 className="font-semibold">{mockPin.title}</h3>
<p className="text-sm text-gray-600 line-clamp-2">{mockPin.description}</p>
</div>
</div>
</div>
{/* Boards */}
<div className="flex-1 overflow-y-auto p-6">
<button className="w-full p-4 border-2 border-dashed border-gray-300 rounded-2xl hover:border-gray-400 mb-4">
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-gray-200 rounded flex items-center justify-center text-2xl">
+
</div>
<span className="font-semibold">Create board</span>
</div>
</button>
{mockBoards.map(board => (
<button
key={board.id}
onClick={() => setSelectedBoardId(board.id)}
className={`w-full p-4 rounded-2xl hover:bg-gray-100 mb-2 ${
selectedBoardId === board.id ? 'bg-gray-100 ring-2 ring-blue-500' : ''
}`}
>
<div className="flex items-center gap-3">
{board.coverImage ? (
<img src={board.coverImage} alt={board.name} className="w-12 h-12 rounded object-cover" />
) : (
<div className="w-12 h-12 bg-gray-200 rounded" />
)}
<div className="flex-1 text-left">
<div className="font-semibold">{board.name}</div>
<div className="text-sm text-gray-600">{board.pinCount} pins</div>
</div>
{selectedBoardId === board.id && <div className="text-blue-500 text-xl">√</div>}
</div>
</button>
))}
</div>
{/* Save Button */}
<div className="p-6 border-t border-gray-200">
<button
disabled={!selectedBoardId}
className="w-full py-3 bg-red-600 text-white rounded-full font-semibold hover:bg-red-700 disabled:bg-gray-300"
>
Save
</button>
</div>
</div>
</div>
);
}Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is Prisma in an application?
2. How do you fetch related records in findMany (e.g. a user's posts)?
Hands-on tasks in the game
- Click in order
Click the elements to build the Prisma install command:
- Code editor
Create a Prisma model and queries
- Vertical ordering
Order the steps of working with the database via Prisma: