Prisma Setup
Complete PostgreSQL database setup with Prisma ORM.
Dependencies#
npm install prisma @prisma/client
npm install -D prismaEnvironment Variables#
# .env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
# For Neon (serverless)
DATABASE_URL="postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/mydb?sslmode=require"Initialize Prisma#
npx prisma initSchema Definition#
1// prisma/schema.prisma
2generator client {
3 provider = "prisma-client-js"
4}
5
6datasource db {
7 provider = "postgresql"
8 url = env("DATABASE_URL")
9 directUrl = env("DIRECT_URL") // For migrations with connection pooling
10}
11
12// User model
13model User {
14 id String @id @default(cuid())
15 clerkId String @unique
16 email String @unique
17 name String?
18 imageUrl String?
19 role Role @default(USER)
20
21 // Relations
22 posts Post[]
23 comments Comment[]
24
25 // Timestamps
26 createdAt DateTime @default(now())
27 updatedAt DateTime @updatedAt
28
29 @@index([email])
30}
31
32enum Role {
33 USER
34 ADMIN
35 MODERATOR
36}
37
38// Post model
39model Post {
40 id String @id @default(cuid())
41 title String
42 slug String @unique
43 content String?
44 published Boolean @default(false)
45
46 // Relations
47 authorId String
48 author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
49 comments Comment[]
50 tags Tag[]
51
52 // Timestamps
53 publishedAt DateTime?
54 createdAt DateTime @default(now())
55 updatedAt DateTime @updatedAt
56
57 @@index([authorId])
58 @@index([slug])
59 @@index([published, createdAt])
60}
61
62// Comment model
63model Comment {
64 id String @id @default(cuid())
65 content String
66
67 // Relations
68 postId String
69 post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
70 authorId String
71 author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
72
73 // Self-relation for replies
74 parentId String?
75 parent Comment? @relation("CommentReplies", fields: [parentId], references: [id])
76 replies Comment[] @relation("CommentReplies")
77
78 createdAt DateTime @default(now())
79 updatedAt DateTime @updatedAt
80
81 @@index([postId])
82 @@index([authorId])
83}
84
85// Tag model (many-to-many)
86model Tag {
87 id String @id @default(cuid())
88 name String @unique
89 slug String @unique
90 posts Post[]
91
92 @@index([slug])
93}Prisma Client Setup#
1// lib/prisma.ts
2import { PrismaClient } from '@prisma/client';
3
4const globalForPrisma = globalThis as unknown as {
5 prisma: PrismaClient | undefined;
6};
7
8export const prisma =
9 globalForPrisma.prisma ??
10 new PrismaClient({
11 log: process.env.NODE_ENV === 'development'
12 ? ['query', 'error', 'warn']
13 : ['error'],
14 });
15
16if (process.env.NODE_ENV !== 'production') {
17 globalForPrisma.prisma = prisma;
18}Generate and Push#
1# Generate Prisma Client
2npx prisma generate
3
4# Push schema to database (development)
5npx prisma db push
6
7# Create migration (production)
8npx prisma migrate dev --name init
9
10# Apply migrations in production
11npx prisma migrate deployCommon Queries#
Create#
1// Create user
2const user = await prisma.user.create({
3 data: {
4 clerkId: 'clerk_123',
5 email: 'user@example.com',
6 name: 'John Doe',
7 },
8});
9
10// Create with relations
11const post = await prisma.post.create({
12 data: {
13 title: 'My Post',
14 slug: 'my-post',
15 content: 'Content here...',
16 author: {
17 connect: { id: userId },
18 },
19 tags: {
20 connectOrCreate: [
21 {
22 where: { slug: 'javascript' },
23 create: { name: 'JavaScript', slug: 'javascript' },
24 },
25 ],
26 },
27 },
28 include: {
29 author: true,
30 tags: true,
31 },
32});Read#
1// Find unique
2const user = await prisma.user.findUnique({
3 where: { email: 'user@example.com' },
4});
5
6// Find with relations
7const post = await prisma.post.findUnique({
8 where: { slug: 'my-post' },
9 include: {
10 author: {
11 select: { id: true, name: true, imageUrl: true },
12 },
13 comments: {
14 include: { author: true },
15 orderBy: { createdAt: 'desc' },
16 take: 10,
17 },
18 tags: true,
19 },
20});
21
22// Find many with filters
23const posts = await prisma.post.findMany({
24 where: {
25 published: true,
26 author: { role: 'ADMIN' },
27 tags: { some: { slug: 'javascript' } },
28 },
29 orderBy: { createdAt: 'desc' },
30 take: 20,
31 skip: 0,
32});
33
34// Count
35const count = await prisma.post.count({
36 where: { published: true },
37});Update#
1// Update one
2const updated = await prisma.post.update({
3 where: { id: postId },
4 data: {
5 title: 'Updated Title',
6 published: true,
7 publishedAt: new Date(),
8 },
9});
10
11// Update many
12const result = await prisma.post.updateMany({
13 where: { authorId: userId },
14 data: { published: false },
15});
16
17// Upsert
18const user = await prisma.user.upsert({
19 where: { clerkId: 'clerk_123' },
20 update: { name: 'Updated Name' },
21 create: {
22 clerkId: 'clerk_123',
23 email: 'new@example.com',
24 name: 'New User',
25 },
26});Delete#
1// Delete one
2await prisma.post.delete({
3 where: { id: postId },
4});
5
6// Delete many
7await prisma.comment.deleteMany({
8 where: { postId },
9});
10
11// Soft delete (using a field)
12await prisma.post.update({
13 where: { id: postId },
14 data: { deletedAt: new Date() },
15});Transactions#
1// Interactive transaction
2const result = await prisma.$transaction(async (tx) => {
3 const post = await tx.post.create({
4 data: { title: 'New Post', slug: 'new-post', authorId: userId },
5 });
6
7 await tx.user.update({
8 where: { id: userId },
9 data: { postCount: { increment: 1 } },
10 });
11
12 return post;
13});
14
15// Batch transaction
16const [posts, users] = await prisma.$transaction([
17 prisma.post.findMany({ where: { published: true } }),
18 prisma.user.findMany({ where: { role: 'ADMIN' } }),
19]);Middleware#
1// lib/prisma.ts
2import { PrismaClient } from '@prisma/client';
3
4const prisma = new PrismaClient();
5
6// Soft delete middleware
7prisma.$use(async (params, next) => {
8 if (params.model === 'Post') {
9 if (params.action === 'delete') {
10 params.action = 'update';
11 params.args.data = { deletedAt: new Date() };
12 }
13 if (params.action === 'deleteMany') {
14 params.action = 'updateMany';
15 params.args.data = { deletedAt: new Date() };
16 }
17 }
18 return next(params);
19});
20
21// Query logging middleware
22prisma.$use(async (params, next) => {
23 const before = Date.now();
24 const result = await next(params);
25 const after = Date.now();
26
27 console.log(`Query ${params.model}.${params.action} took ${after - before}ms`);
28
29 return result;
30});
31
32export { prisma };Scripts#
1// package.json
2{
3 "scripts": {
4 "db:generate": "prisma generate",
5 "db:push": "prisma db push",
6 "db:migrate": "prisma migrate dev",
7 "db:migrate:deploy": "prisma migrate deploy",
8 "db:studio": "prisma studio",
9 "db:seed": "prisma db seed",
10 "db:reset": "prisma migrate reset"
11 },
12 "prisma": {
13 "seed": "tsx prisma/seed.ts"
14 }
15}Seed Script#
1// prisma/seed.ts
2import { PrismaClient } from '@prisma/client';
3
4const prisma = new PrismaClient();
5
6async function main() {
7 // Create admin user
8 const admin = await prisma.user.upsert({
9 where: { email: 'admin@example.com' },
10 update: {},
11 create: {
12 clerkId: 'admin_clerk_id',
13 email: 'admin@example.com',
14 name: 'Admin User',
15 role: 'ADMIN',
16 },
17 });
18
19 // Create tags
20 const tags = await Promise.all(
21 ['JavaScript', 'TypeScript', 'React', 'Next.js'].map((name) =>
22 prisma.tag.upsert({
23 where: { slug: name.toLowerCase().replace(/\./g, '') },
24 update: {},
25 create: {
26 name,
27 slug: name.toLowerCase().replace(/\./g, ''),
28 },
29 })
30 )
31 );
32
33 // Create sample posts
34 await prisma.post.createMany({
35 data: [
36 {
37 title: 'Getting Started with Next.js',
38 slug: 'getting-started-nextjs',
39 content: 'Learn how to build apps with Next.js...',
40 published: true,
41 publishedAt: new Date(),
42 authorId: admin.id,
43 },
44 {
45 title: 'TypeScript Best Practices',
46 slug: 'typescript-best-practices',
47 content: 'Tips for writing better TypeScript...',
48 published: true,
49 publishedAt: new Date(),
50 authorId: admin.id,
51 },
52 ],
53 skipDuplicates: true,
54 });
55
56 console.log('Seed completed');
57}
58
59main()
60 .catch((e) => {
61 console.error(e);
62 process.exit(1);
63 })
64 .finally(async () => {
65 await prisma.$disconnect();
66 });