Next.js with Prisma — Database Integration
Advertisement
Next.js with Prisma — Database Integration
Prisma provides type-safe database access with an intuitive query interface, making data management in Next.js applications straightforward.
- Next.js with Prisma — Database Integration
- Installation
- Database Connection
- Schema Definition
- Migrations
- Prisma Client Setup
- CRUD Operations
- API Routes with Prisma
- Dynamic Routes
- Advanced Queries
- Transactions
- Middleware & Hooks
- Real-World Example: Blog
- FAQ
Installation
npm install @prisma/client
npm install -D prisma
npx prisma init
Database Connection
Set environment variable:
# .env.local
DATABASE_URL="postgresql://user:password@localhost:5432/dbname"
Schema Definition
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Migrations
# Create migration
npx prisma migrate dev --name init
# Apply migrations
npx prisma migrate deploy
# Reset database
npx prisma migrate reset
Prisma Client Setup
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = global as unknown as { prisma: PrismaClient }
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: ['query']
})
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
CRUD Operations
Create:
// Create a user
const user = await prisma.user.create({
data: {
email: 'user@example.com',
name: 'John Doe'
}
})
// Create with relations
const post = await prisma.post.create({
data: {
title: 'My Post',
content: 'Content here',
author: {
connect: { id: userId }
}
}
})
Read:
// Find one
const user = await prisma.user.findUnique({
where: { email: 'user@example.com' }
})
// Find many
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: true },
orderBy: { createdAt: 'desc' }
})
// Find first
const latestPost = await prisma.post.findFirst({
orderBy: { createdAt: 'desc' }
})
Update:
const user = await prisma.user.update({
where: { id: userId },
data: { name: 'Jane Doe' }
})
Delete:
await prisma.post.delete({
where: { id: postId }
})
API Routes with Prisma
// app/api/posts/route.ts
import { prisma } from '@/lib/prisma'
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
const posts = await prisma.post.findMany({
include: { author: true },
orderBy: { createdAt: 'desc' }
})
return NextResponse.json(posts)
}
export async function POST(request: NextRequest) {
const { title, content, authorId } = await request.json()
const post = await prisma.post.create({
data: { title, content, authorId },
include: { author: true }
})
return NextResponse.json(post, { status: 201 })
}
Dynamic Routes
// app/blog/[id]/page.tsx
import { prisma } from '@/lib/prisma'
import { notFound } from 'next/navigation'
export async function generateStaticParams() {
const posts = await prisma.post.findMany({
select: { id: true }
})
return posts.map(post => ({
id: post.id.toString()
}))
}
export default async function PostPage({ params }) {
const post = await prisma.post.findUnique({
where: { id: parseInt(params.id) },
include: { author: true }
})
if (!post) notFound()
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
<p>By {post.author.name}</p>
</article>
)
}
Advanced Queries
// Complex queries
const user = await prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 10
}
}
})
// Aggregation
const count = await prisma.post.count({
where: { published: true }
})
// Grouping
const grouped = await prisma.post.groupBy({
by: ['authorId'],
_count: true
})
// Raw queries
const results = await prisma.$queryRaw`
SELECT * FROM "Post" WHERE published = true
`
Transactions
// Multiple operations as transaction
const [user, post] = await prisma.$transaction([
prisma.user.create({
data: { email: 'new@example.com' }
}),
prisma.post.create({
data: {
title: 'Post',
authorId: userId
}
})
])
// Or with callback
await prisma.$transaction(async (tx) => {
const user = await tx.user.update({
where: { id: userId },
data: { name: 'Updated' }
})
await tx.post.update({
where: { id: postId },
data: { authorId: user.id }
})
})
Middleware & Hooks
// prisma/schema.prisma - Add timestamps
model Post {
// ... other fields
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Use in queries
const recentPosts = await prisma.post.findMany({
where: {
createdAt: {
gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
}
}
})
Real-World Example: Blog
// lib/db.ts
import { prisma } from './prisma'
export async function getPosts(page = 1, limit = 10) {
const skip = (page - 1) * limit
const [posts, total] = await Promise.all([
prisma.post.findMany({
where: { published: true },
include: { author: { select: { id: true, name: true } } },
orderBy: { createdAt: 'desc' },
skip,
take: limit
}),
prisma.post.count({ where: { published: true } })
])
return { posts, total, pages: Math.ceil(total / limit) }
}
export async function getPostBySlug(slug: string) {
return prisma.post.findUnique({
where: { slug },
include: {
author: true,
comments: {
include: { author: true },
orderBy: { createdAt: 'desc' }
}
}
})
}
export async function createPost(data: PostData, authorId: number) {
return prisma.post.create({
data: { ...data, authorId },
include: { author: true }
})
}
FAQ
Q: How do I handle Prisma type safety in TypeScript? A: Prisma generates types automatically. Import from @prisma/client for full type safety.
Q: Should I use raw SQL or Prisma queries? A: Use Prisma queries for safety. Raw SQL only when necessary and properly parameterized.
Q: How do I optimize slow queries? A: Use include/select to fetch only needed fields, add database indexes, and use skip/take for pagination.
Prisma transforms database interactions into type-safe, productive development.
Advertisement