PostgreSQL ve Prisma ORM: Modern Veritabanı Yönetimi

PostgreSQL ve Prisma ORM: Modern Veritabanı Yönetimi
PostgreSQL, güvenilirliği ve gelişmiş özellikleriyle tanınan açık kaynaklı bir ilişkisel veritabanı sistemidir. Prisma ise Node.js ve TypeScript ekosisteminde yeni nesil bir ORM olarak öne çıkar. Type-safe sorgular, otomatik migration'lar ve güçlü bir schema tanımlama dili sunan Prisma, geleneksel ORM'lerin karmaşıklığını ortadan kaldırır. Bu rehberde PostgreSQL ve Prisma'yı birlikte kullanarak modern, güvenli ve performanslı bir veritabanı katmanı oluşturmayı adım adım öğreneceksiniz.
1. Prisma Kurulumu ve Schema Tanımlama
Prisma, schema-first yaklaşımı benimser. prisma/schema.prisma dosyasında modellerinizi, ilişkilerinizi ve veritabanı bağlantınızı tanımlarsınız. Prisma CLI bu schema'dan veritabanı migration'larını ve type-safe client'ı otomatik olarak oluşturur.
# Prisma kurulumu
npm install prisma @prisma/client
npx prisma init
# prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String
password String
role Role @default(USER)
avatar String?
bio String? @db.Text
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
comments Comment[]
profile Profile?
@@index([email])
@@index([createdAt(sort: Desc)])
@@map("users")
}
model Post {
id String @id @default(cuid())
title String
slug String @unique
content String @db.Text
excerpt String? @db.VarChar(300)
status PostStatus @default(DRAFT)
viewCount Int @default(0)
publishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
categories Category[]
comments Comment[]
tags Tag[]
@@index([authorId])
@@index([status, publishedAt(sort: Desc)])
@@map("posts")
}
model Comment {
id String @id @default(cuid())
text String @db.Text
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId String
@@map("comments")
}
model Category {
id String @id @default(cuid())
name String @unique
slug String @unique
posts Post[]
@@map("categories")
}
model Tag {
id String @id @default(cuid())
name String @unique
posts Post[]
@@map("tags")
}
model Profile {
id String @id @default(cuid())
website String?
github String?
twitter String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String @unique
@@map("profiles")
}
enum Role {
USER
EDITOR
ADMIN
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
2. Migration ve Veritabanı Yönetimi
Prisma Migrate, schema değişikliklerinizi SQL migration dosyalarına dönüştürür ve veritabanınıza uygular. Her migration bir SQL dosyası olarak saklanır, bu da değişikliklerin izlenebilir ve geri alınabilir olmasını sağlar.
# İlk migration oluştur ve uygula
npx prisma migrate dev --name init
# Schema değişikliği sonrası yeni migration
npx prisma migrate dev --name add_tags_table
# Production'da migration uygula
npx prisma migrate deploy
# Prisma Client'ı yeniden oluştur
npx prisma generate
# Veritabanını görselleştir
npx prisma studio
# Seed dosyası oluşturma (prisma/seed.js)
const { PrismaClient } = require('@prisma/client');
const bcrypt = require('bcryptjs');
const prisma = new PrismaClient();
async function seed() {
// Mevcut verileri temizle
await prisma.comment.deleteMany();
await prisma.post.deleteMany();
await prisma.user.deleteMany();
// Admin kullanıcı
const admin = await prisma.user.create({
data: {
email: 'admin@example.com',
name: 'Admin User',
password: await bcrypt.hash('admin123', 12),
role: 'ADMIN',
profile: {
create: {
website: 'https://example.com',
github: 'https://github.com/admin'
}
}
}
});
// Kategoriler
const categories = await Promise.all(
['Teknoloji', 'Backend', 'Frontend'].map(name =>
prisma.category.create({
data: { name, slug: name.toLowerCase() }
})
)
);
// Örnek yazılar
await prisma.post.create({
data: {
title: 'Prisma ile Veritabanı Yönetimi',
slug: 'prisma-veritabani-yonetimi',
content: 'Prisma modern bir ORM çözümüdür...',
status: 'PUBLISHED',
publishedAt: new Date(),
authorId: admin.id,
categories: { connect: [{ id: categories[0].id }] }
}
});
console.log('Seed verileri oluşturuldu');
}
seed()
.catch(console.error)
.finally(() => prisma.$disconnect());
3. CRUD Operasyonları ve Gelişmiş Sorgular
Prisma Client, type-safe sorgular ile veritabanı işlemlerini güvenli ve kolay hale getirir. TypeScript entegrasyonu sayesinde sorgu hataları derleme zamanında yakalanır, bu da runtime hatalarını önemli ölçüde azaltır.
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error']
});
// CREATE
async function createPost(data) {
return prisma.post.create({
data: {
title: data.title,
slug: data.slug,
content: data.content,
author: { connect: { id: data.authorId } },
categories: {
connect: data.categoryIds.map(id => ({ id }))
},
tags: {
connectOrCreate: data.tags.map(tag => ({
where: { name: tag },
create: { name: tag }
}))
}
},
include: {
author: { select: { name: true, avatar: true } },
categories: true,
tags: true
}
});
}
// READ - Gelişmiş filtreleme ve sayfalama
async function getPosts({ page = 1, limit = 10, search, status, authorId }) {
const where = {
...(status && { status }),
...(authorId && { authorId }),
...(search && {
OR: [
{ title: { contains: search, mode: 'insensitive' } },
{ content: { contains: search, mode: 'insensitive' } }
]
})
};
const [posts, total] = await Promise.all([
prisma.post.findMany({
where,
include: {
author: { select: { id: true, name: true, avatar: true } },
categories: { select: { name: true, slug: true } },
_count: { select: { comments: true } }
},
orderBy: { publishedAt: 'desc' },
skip: (page - 1) * limit,
take: limit
}),
prisma.post.count({ where })
]);
return { data: posts, total, pages: Math.ceil(total / limit) };
}
// UPDATE
async function updatePost(id, data) {
return prisma.post.update({
where: { id },
data: {
...data,
...(data.status === 'PUBLISHED' && !data.publishedAt && {
publishedAt: new Date()
})
}
});
}
// Transaction kullanımı
async function publishPostWithNotification(postId) {
return prisma.$transaction(async (tx) => {
const post = await tx.post.update({
where: { id: postId },
data: { status: 'PUBLISHED', publishedAt: new Date() },
include: { author: true }
});
await tx.user.update({
where: { id: post.authorId },
data: { /* bildirim verisi */ }
});
return post;
});
}
4. Raw SQL ve Performans Optimizasyonu
Prisma, karmaşık sorgular için raw SQL desteği sunar. Ayrıca select ve include ile sadece ihtiyaç duyduğunuz alanları çekerek over-fetching'i önleyebilirsiniz. Veritabanı index'leri, sorgu performansını dramatik şekilde artırır ve schema dosyasında tanımlanabilir.
N+1 sorgu problemini çözmek için Prisma'nın include ve select özelliklerini kullanın. Büyük veri setlerinde cursor-based pagination, offset pagination'dan çok daha performanslıdır. Prisma'nın query engine'i, ilişkili verileri otomatik olarak batch'leyerek veritabanı round-trip'lerini minimize eder.
Sonuç
PostgreSQL ve Prisma kombinasyonu, Node.js uygulamalarında güçlü, type-safe ve bakımı kolay bir veritabanı katmanı oluşturmanızı sağlar. Prisma'nın schema-first yaklaşımı, otomatik migration'ları ve type-safe client'ı ile veritabanı yönetimi büyük ölçüde kolaylaşır. Bu rehberdeki kavramları uygulamaya koyarak production-ready bir veritabanı altyapısı kurabilirsiniz.