Next.js ile Drizzle ORM + PostgreSQL: Prisma'dan Daha İyi mi?
Drizzle ORM kurulum, schema tanımlama, migration (push/generate/migrate farkı), CRUD sorgular, transactions ve Server Components desenleri. Prisma karşılaştırması dahil.

Önemli Not: Bu yazıdaki teknik bilgiler yazım tarihi itibarıyla geçerlidir. Kullanılan kütüphaneler, API'ler ve servisler zaman içinde değişebilir. Ücretlendirme, yasal düzenleme ve vergi konularında ilgili resmi kaynakları ve uzmanları referans alınız. Bu içerik bilgilendirme amaçlı olup herhangi bir finansal veya hukuki tavsiye niteliği taşımamaktadır.
Next.js projesine veritabanı ekleyeceksiniz. Prisma mı, Drizzle ORM mi, TypeORM mı seçmelisiniz? Bu rehberde Drizzle ORM'yi sıfırdan kurarak, gerçek production senaryolarında nasıl kullanıldığını öğreneceksiniz.
Bu projenin (ilkkod.com) müşteri portalı da Drizzle ORM + Neon PostgreSQL ile çalışıyor. Aktif production deneyimiyle yazılmış bir rehber.
Drizzle ORM Nedir?
Drizzle ORM, TypeScript ile yazılmış, hafif ve SQL'e yakın bir ORM (Object-Relational Mapper) kütüphanesidir. 2022'de yayımlanan bu kütüphane, Prisma'nın sunduğu developer experience'ı korurken ciddi teknik avantajlar sunuyor.
Temel farkı: Drizzle, "ORM ama SQL bilincinde" tasarlanmış. Prisma sizi SQL'den uzaklaştırırken, Drizzle TypeScript yazarken SQL düşüncesini korur.
ORM Ekosistemi Karşılaştırması (2026)
| ORM | Bundle Size | TypeScript | Edge Uyumlu | SQL Gücü |
|---|---|---|---|---|
| Drizzle ORM | ~35KB | ✅ Native | ✅ Tam | ✅ SQL benzeri API |
| Prisma | ~15MB (engine dahil) | ✅ İyi | ⚠️ Kısıtlı | ⚠️ Abstracted |
| TypeORM | ~400KB | ✅ İyi | ❌ Hayır | ✅ İyi |
| Sequelize | ~600KB | ⚠️ Topluluk | ❌ Hayır | ✅ İyi |
| Kysely | ~30KB | ✅ Mükemmel | ✅ Tam | ✅ Query builder |
Neden Drizzle? 5 Güçlü Neden
1. Bundle Size Avantajı
Prisma, Rust tabanlı bir query engine binary içerir (~15MB). Serverless ortamlarda bu hem cold start süresini uzatır hem de Vercel gibi platformlarda boyut limitlerine yaklaşır. Drizzle'ın JavaScript runtime'a bağımlılığı yok — saf TypeScript.
2. TypeScript-First Tasarım
Drizzle şeması, doğrudan TypeScript tiplerini üretir. Ayrı prisma generate adımına gerek yok:
// Şema tanımladığınızda tipler anında hazır
const users = pgTable('users', {
id: serial('id').primaryKey(),
name: varchar('name', { length: 255 }),
})
// $inferSelect → SELECT tipini çıkarır
type User = typeof users.$inferSelect
// { id: number; name: string | null }
// $inferInsert → INSERT tipini çıkarır
type NewUser = typeof users.$inferInsert
// { id?: number; name?: string | null }
3. SQL'e Yakın API
Drizzle sorgular SQL'e birebir eşlenir. SQL bilen biri Drizzle'ı anında kavrar:
// SQL: SELECT * FROM users WHERE age > 18 ORDER BY name LIMIT 10
const result = await db
.select()
.from(users)
.where(gt(users.age, 18))
.orderBy(asc(users.name))
.limit(10)
Prisma'nın soyutlamaları güçlüdür, ancak karmaşık sorgularda $queryRaw kullanmak zorunda kalırsınız. Drizzle'da bu durum çok nadir.
4. Edge Runtime Uyumluluğu
Neon HTTP driver ile birlikte Drizzle, Next.js Edge Runtime ve Cloudflare Workers'ta sorunsuz çalışır. Prisma'nın edge desteği hâlâ sınırlı ve karmaşık.
5. Prisma'nın Gerektirdiği Adım Yok
Prisma, kod değişikliklerinde prisma generate çalıştırmanızı zorunlu kılar. CI/CD pipeline'ında unutulabilir. Drizzle'da böyle bir adım yok — TypeScript derlemesi tiplerini üretir.
Kurulum
Projenizde Bun kullanıyorsanız (önerilen):
bun add drizzle-orm @neondatabase/serverless bun add -d drizzle-kit
Node.js ile:
npm install drizzle-orm @neondatabase/serverless npm install --save-dev drizzle-kit
Versiyon notu (Mart 2026):
drizzle-ormv1.0.0-beta.18,drizzle-kit0.31.10. Beta işareti "deneysel" değil — production'da kullanıma hazır, API kararlı.drizzle-kit 0.31.10'da native Bun desteği eklendi.
Neon yerine standart PostgreSQL kullanıyorsanız:
bun add drizzle-orm pg bun add -d drizzle-kit @types/pg
drizzle.config.ts Konfigürasyonu
Proje köküne drizzle.config.ts dosyası ekleyin:
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
schema: './lib/db/schema/index.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
})
Parametre açıklamaları:
schema: Şema dosyanızın yolu. Tek dosya veya glob pattern ('./src/db/schema/**/*.ts') olabilir.out: Migration SQL dosyalarının çıktı klasörü.dialect:'postgresql','mysql','sqlite'seçenekleri mevcut.dbCredentials.url: Ortam değişkeninizden alınan connection string.
Schema Tanımlama
Drizzle şemaları saf TypeScript'tir. Hiçbir dekoratör veya özel syntax yok.
Temel Tablo Tanımı
// lib/db/schema/users.ts
import {
pgTable,
serial,
varchar,
text,
integer,
boolean,
timestamp,
json,
uuid,
} from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
uuid: uuid('uuid').defaultRandom().notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }).notNull().unique(),
bio: text('bio'),
age: integer('age'),
isActive: boolean('is_active').default(true).notNull(),
metadata: json('metadata').$type<Record<string, unknown>>(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
})
Enum Tanımlama
import { pgEnum, pgTable, serial, varchar } from 'drizzle-orm/pg-core'
// PostgreSQL native enum
export const statusEnum = pgEnum('status', ['pending', 'active', 'cancelled'])
export const projects = pgTable('projects', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 255 }).notNull(),
status: statusEnum('status').default('pending').notNull(),
})
Relations Tanımlama
Drizzle'da relations, veritabanında foreign key oluşturmaz — sorgu API'si için tip güvenliği sağlar:
// lib/db/schema/relations.ts
import { relations } from 'drizzle-orm'
import { users } from './users'
import { posts } from './posts'
import { comments } from './comments'
// One-to-Many: Bir user, birden fazla post'a sahip olabilir
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}))
// Many-to-One + One-to-Many
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
comments: many(comments),
}))
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, {
fields: [comments.postId],
references: [posts.id],
}),
author: one(users, {
fields: [comments.authorId],
references: [users.id],
}),
}))
Foreign key constraint eklemek için:
import { pgTable, serial, integer, references } from 'drizzle-orm/pg-core'
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: varchar('title', { length: 500 }).notNull(),
authorId: integer('author_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
})
Index Tanımlama
import { pgTable, varchar, index, uniqueIndex } from 'drizzle-orm/pg-core'
export const products = pgTable('products', {
id: serial('id').primaryKey(),
slug: varchar('slug', { length: 255 }).notNull(),
categoryId: integer('category_id'),
}, (table) => [
uniqueIndex('products_slug_idx').on(table.slug),
index('products_category_idx').on(table.categoryId),
])
Schema Index Dosyası
// lib/db/schema/index.ts
export * from './users'
export * from './posts'
export * from './comments'
export * from './relations'
Migration Yönetimi: push vs generate vs migrate
Bu üç komut en çok karıştırılan konudur. Ne zaman hangisini kullanacağınızı net şekilde bilin:
| Komut | Ne Yapar | Ne Zaman |
|---|---|---|
bunx drizzle-kit push | Schema'yı direkt DB'ye uygular, migration dosyası oluşturmaz | Geliştirme / prototipleme |
bunx drizzle-kit generate | SQL migration dosyaları oluşturur (drizzle/ klasörüne) | Her schema değişikliğinde |
bunx drizzle-kit migrate | generate ile oluşturulan SQL'leri DB'ye uygular | Production deploy öncesi |
Geliştirme Ortamı İş Akışı
# Şemayı değiştirdiniz → direkt push (hızlı, pratik)
bunx drizzle-kit push
# Veritabanı arayüzü açmak için
bunx drizzle-kit studio
Production İş Akışı
# 1. Migration dosyası oluştur (Git'e commit edilir)
bunx drizzle-kit generate
# 2. SQL dosyasını incele: drizzle/0001_xxx.sql
# 3. Deploy sonrası migration'ı uygula
bunx drizzle-kit migrate
Neden push production'da kullanmayın? Çünkü migration geçmişi oluşturmaz. Hangi değişikliklerin ne zaman uygulandığını takip edemezsiniz. generate + migrate akışı, veritabanı değişikliklerini kod değişiklikleriyle birlikte versiyonlar.
Veritabanı Bağlantısı
Neon HTTP Driver (Önerilen — Serverless)
// lib/db/index.ts
import { neon } from '@neondatabase/serverless'
import { drizzle } from 'drizzle-orm/neon-http'
import * as schema from './schema'
const sql = neon(process.env.DATABASE_URL!)
export const db = drizzle(sql, { schema })
Bu driver Edge Runtime uyumlu, serverless için optimize edilmiş. Her sorgu bağımsız HTTP isteği yapar. Single transaction senaryoları için mükemmel.
Neon WebSocket Driver (Transaction'lar için)
// lib/db/index.ts — Interactive transaction ihtiyacı varsa
import { Pool } from '@neondatabase/serverless'
import { drizzle } from 'drizzle-orm/neon-serverless'
import * as schema from './schema'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export const db = drizzle(pool, { schema })
Önemli: WebSocket driver Edge Runtime'da çalışmaz. Yalnızca Node.js runtime'da kullanın.
Hangi driver ne zaman?
- Server Components, Route Handlers, Server Actions → HTTP driver (her zaman güvenli)
- Karmaşık transactions (birden fazla operasyon) → WebSocket driver (Node.js runtime)
- Edge Middleware / Cloudflare Workers → HTTP driver (tek seçenek)
CRUD Sorgular
SELECT — Veri Okuma
import { db } from '@/lib/db'
import { users } from '@/lib/db/schema'
// Tüm kullanıcıları getir
const allUsers = await db.select().from(users)
// Belirli alanları seç
const userNames = await db.select({
id: users.id,
name: users.name,
email: users.email,
}).from(users)
// Tek kayıt (findFirst)
const user = await db
.select()
.from(users)
.where(eq(users.email, 'hello@example.com'))
.limit(1)
.then(rows => rows[0] ?? null)
INSERT — Veri Ekleme
import { eq } from 'drizzle-orm'
// Tek kayıt ekle + geri döndür
const [newUser] = await db
.insert(users)
.values({
name: 'Ahmet Yılmaz',
email: 'ahmet@example.com',
})
.returning()
// Çoklu kayıt ekle
await db.insert(users).values([
{ name: 'Mehmet', email: 'mehmet@example.com' },
{ name: 'Ayşe', email: 'ayse@example.com' },
])
// Conflict handling (upsert)
await db
.insert(users)
.values({ email: 'ahmet@example.com', name: 'Ahmet' })
.onConflictDoUpdate({
target: users.email,
set: { name: 'Ahmet Güncellendi', updatedAt: new Date() },
})
UPDATE — Veri Güncelleme
// Belirli kaydı güncelle
const [updatedUser] = await db
.update(users)
.set({ name: 'Yeni İsim', updatedAt: new Date() })
.where(eq(users.id, 1))
.returning()
// Koşullu güncelleme
await db
.update(users)
.set({ isActive: false })
.where(and(
eq(users.isActive, true),
lt(users.lastLoginAt, new Date(Date.now() - 90 * 24 * 60 * 60 * 1000)),
))
DELETE — Veri Silme
// Tek kayıt sil
const [deleted] = await db
.delete(users)
.where(eq(users.id, 1))
.returning()
// Toplu silme
await db.delete(users).where(eq(users.isActive, false))
Filtreleme ve Sayfalama
import { eq, ne, gt, gte, lt, lte, like, ilike, and, or, isNull, isNotNull, inArray, between } from 'drizzle-orm'
// Karmaşık where koşulu
const results = await db
.select()
.from(users)
.where(
and(
eq(users.isActive, true),
or(
like(users.name, '%Ahmet%'),
ilike(users.email, '%@gmail.com'), // case-insensitive
),
isNotNull(users.updatedAt),
gt(users.age, 18),
)
)
.orderBy(asc(users.name), desc(users.createdAt))
.limit(20)
.offset(40) // Sayfa 3 (0-indexed, her sayfada 20 kayıt)
// IN operatörü
const admins = await db
.select()
.from(users)
.where(inArray(users.id, [1, 2, 3, 4, 5]))
// BETWEEN
const recentUsers = await db
.select()
.from(users)
.where(between(users.createdAt, new Date('2026-01-01'), new Date('2026-03-01')))
Sayfalama Yardımcısı
// Cursor-based pagination (büyük veri setleri için önerilen)
async function getUsers(cursor?: number, pageSize = 20) {
return db
.select()
.from(users)
.where(cursor ? gt(users.id, cursor) : undefined)
.orderBy(asc(users.id))
.limit(pageSize)
}
// Offset-based pagination
async function getUsersPage(page: number, pageSize = 20) {
return db
.select()
.from(users)
.orderBy(desc(users.createdAt))
.limit(pageSize)
.offset(page * pageSize)
}
İlişkili Veri: Join ve Relations API
SQL-Style Join
import { eq } from 'drizzle-orm'
// Inner join
const postsWithAuthors = await db
.select({
postId: posts.id,
postTitle: posts.title,
authorName: users.name,
authorEmail: users.email,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.status, 'published'))
.orderBy(desc(posts.createdAt))
// Left join (eşleşme olmasa da post getirir)
const allPostsWithCommentCount = await db
.select({
postId: posts.id,
title: posts.title,
})
.from(posts)
.leftJoin(comments, eq(comments.postId, posts.id))
Relations API (Önerilen)
Relations tanımladıysanız, db.query API'si daha okunabilir sorgular sunar:
// Kullanıcıyı post'larıyla birlikte getir
const userWithPosts = await db.query.users.findFirst({
where: eq(users.id, 1),
with: {
posts: {
where: eq(posts.status, 'published'),
orderBy: desc(posts.createdAt),
limit: 5,
with: {
comments: {
limit: 3,
},
},
},
},
})
// findMany ile filtreleme
const activeUsersWithPosts = await db.query.users.findMany({
where: eq(users.isActive, true),
with: { posts: true },
limit: 10,
})
Transactions
Transaction, birden fazla veritabanı işleminin atomik olarak yürütülmesini sağlar. Ya hepsi başarılı olur, ya da hepsi geri alınır.
// Sipariş oluşturma: atomik işlem
async function createOrder(userId: number, items: OrderItem[]) {
return await db.transaction(async (tx) => {
// 1. Sipariş kaydı oluştur
const [order] = await tx
.insert(orders)
.values({ userId, status: 'pending' })
.returning()
// 2. Sipariş kalemlerini ekle
await tx.insert(orderItems).values(
items.map(item => ({
orderId: order.id,
productId: item.productId,
quantity: item.quantity,
}))
)
// 3. Stok düş
for (const item of items) {
const result = await tx
.update(products)
.set({ stock: sql`${products.stock} - ${item.quantity}` })
.where(and(
eq(products.id, item.productId),
gte(products.stock, item.quantity), // Yeterli stok kontrolü
))
.returning({ id: products.id })
if (result.length === 0) {
throw new Error(`Ürün ${item.productId} için yeterli stok yok`)
// Hata fırlatıldığında transaction otomatik rollback yapar
}
}
return order
})
}
Transaction ile WebSocket driver: HTTP driver transaction'ları desteklemez. Karmaşık transaction'lar için WebSocket driver veya standart pg paketi kullanın.
Next.js Server Components + Drizzle
Server Components, veritabanı çağrılarını doğrudan component içinde yapmanıza olanak verir. Bu, Next.js + Drizzle kombinasyonunun en güçlü yanlarından biridir.
// app/dashboard/page.tsx — Doğrudan DB erişimi, API katmanı gerekmez
import { db } from '@/lib/db'
import { projects, invoices } from '@/lib/db/schema'
import { eq, desc } from 'drizzle-orm'
export default async function DashboardPage() {
// Server Component'da direkt DB sorgusu
const recentProjects = await db.query.projects.findMany({
orderBy: desc(projects.createdAt),
limit: 5,
with: { client: true },
})
const pendingInvoices = await db
.select()
.from(invoices)
.where(eq(invoices.status, 'pending'))
return (
<div>
<section>
<h2>Son Projeler</h2>
{recentProjects.map(project => (
<div key={project.id}>{project.title}</div>
))}
</section>
<section>
<h2>Bekleyen Faturalar</h2>
<p>{pendingInvoices.length} fatura bekliyor</p>
</section>
</div>
)
}
Paralel Veri Çekme
// Birden fazla sorguyu paralel çalıştır
export default async function AnalyticsPage() {
const [users, projects, revenue] = await Promise.all([
db.select({ count: sql<number>`count(*)` }).from(usersTable),
db.select({ count: sql<number>`count(*)` }).from(projectsTable),
db.select({ total: sql<number>`sum(amount)` }).from(invoicesTable)
.where(eq(invoicesTable.status, 'paid')),
])
return <StatsGrid stats={{ users, projects, revenue }} />
}
Caching ile Kullanım
// Next.js 16'da "use cache" directive ile
import { unstable_cache as cache } from 'next/cache'
const getPublishedPosts = cache(
async () => {
return db.query.posts.findMany({
where: eq(posts.status, 'published'),
orderBy: desc(posts.createdAt),
})
},
['published-posts'],
{ revalidate: 3600 } // 1 saat cache
)
export default async function BlogPage() {
const posts = await getPublishedPosts()
return <PostList posts={posts} />
}
Server Actions ile Form + Veritabanı
Server Actions, form submit'i doğrudan server-side kod çalıştırarak işlemenizi sağlar. API route yazmaya gerek kalmaz.
// app/projects/new/page.tsx
import { db } from '@/lib/db'
import { projects } from '@/lib/db/schema'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
async function createProject(formData: FormData) {
'use server'
const title = formData.get('title') as string
const description = formData.get('description') as string
// Basit validasyon
if (!title || title.length < 3) {
throw new Error('Proje başlığı en az 3 karakter olmalı')
}
// Veritabanına kaydet
const [project] = await db
.insert(projects)
.values({ title, description })
.returning()
// Cache geçersiz kıl ve yönlendir
revalidatePath('/projects')
redirect(`/projects/${project.id}`)
}
export default function NewProjectPage() {
return (
<form action={createProject}>
<input name="title" placeholder="Proje Başlığı" required />
<textarea name="description" placeholder="Açıklama" />
<button type="submit">Oluştur</button>
</form>
)
}
Zod ile Validasyon + Server Action
'use server'
import { z } from 'zod'
import { db } from '@/lib/db'
import { projects } from '@/lib/db/schema'
const projectSchema = z.object({
title: z.string().min(3).max(255),
description: z.string().optional(),
clientId: z.coerce.number().positive(),
})
export async function createProjectAction(formData: FormData) {
const raw = Object.fromEntries(formData)
const parsed = projectSchema.safeParse(raw)
if (!parsed.success) {
return { error: parsed.error.flatten() }
}
const [project] = await db
.insert(projects)
.values(parsed.data)
.returning()
return { success: true, project }
}
Tip Güvenliği: Drizzle Şemasından Zod Validator
drizzle-zod paketi v0.30.0'dan itibaren deprecated durumdadır — işlevsellik Drizzle ORM core'a taşınıyor. Şu anki API:
import { createInsertSchema, createSelectSchema, createUpdateSchema } from 'drizzle-zod'
import { users } from '@/lib/db/schema'
// Şemadan otomatik Zod validator üret
const insertUserSchema = createInsertSchema(users, {
// Belirli alanları override edebilirsiniz
email: z.string().email('Geçerli email giriniz'),
name: z.string().min(2, 'İsim en az 2 karakter olmalı'),
})
const updateUserSchema = createUpdateSchema(users)
// Tipler
type InsertUser = z.infer<typeof insertUserSchema>
type UpdateUser = z.infer<typeof updateUserSchema>
// Kullanım
const validated = insertUserSchema.parse(formData)
await db.insert(users).values(validated)
Prisma'dan Drizzle'a Geçiş Neden Düşünmeli?
Prisma yıllardır kurumsal ORM standardıydı. Drizzle onu tamamen geçersiz kılmıyor — ikisi de üretim için uygun. Ancak bazı senaryolarda Drizzle belirgin biçimde öne çıkıyor:
| Senaryo | Önerilen |
|---|---|
| Yeni Next.js projesi + Vercel deploy | Drizzle |
| Edge Functions / Cloudflare Workers | Drizzle |
| Büyük, legacy enterprise proje | Prisma (mevcut altyapı) |
| Karmaşık SQL sorguları çok fazla | Drizzle |
| Takım Prisma deneyimli | Prisma (öğrenme eğrisi) |
| Serverless, soğuk başlatma kritik | Drizzle |
Gerçek konuşmak gerekirse: Drizzle'ın API'si başlangıçta biraz daha dikkat ister, çünkü SQL düşüncesini gerektiriyor. Prisma daha soyut ve "sadece çalış" deneyimi sunuyor. Ama SQL bilen bir geliştirici için Drizzle doğal ve güçlü hissettiriyor.
Production Checklist
Migration ve deployment öncesi bu listeyi gözden geçirin:
Schema Tasarımı
- Tüm foreign key'lerde
.references()kullanıldı - Sık sorgulanan kolonlarda index var (
index(),uniqueIndex()) -
createdAtveupdatedAtalanları var,.defaultNow()ayarlı - UUID veya serial primary key seçimi bilinçli yapıldı
Migration
-
drizzle-kit pushyerinegenerate + migrateakışı kullanılıyor - Migration SQL dosyaları Git'e commit edildi
- Production'da migration
deployscript'e eklendi
Bağlantı
-
DATABASE_URLenvironment variable güvenli ortamda - Neon'da connection pooling aktif (serverless için kritik)
- Transaction'lar için WebSocket driver, diğerleri için HTTP driver
Güvenlik
- Kullanıcı girdisi doğrudan sorguya geçmiyor (SQL injection riski yok — Drizzle parameterized query kullanır, ama yine de kontrol edin)
-
sqltemplate literal kullanıyorsanız değerler interpolate edilmiş mi? - Hassas kolonlar (
passwordHash, vb.) select'te açıkça hariç tutulmuş
Performans
- N+1 sorgu problemi yok — Relations API ile
with: {}kullanıldı - Büyük veri setlerinde
limitvar -
Promise.allile bağımsız sorgular paralel çalıştırılıyor
Sıkça Sorulan Sorular
Drizzle ORM gerçekten production-ready mi? "beta" etiketine güvenebilir miyim?
Evet. drizzle-orm v1.0.0-beta.18 etiketini taşıyor, ancak bu "deneysel" anlamına gelmiyor — API kararlı, sadece major v1.0.0 sürümüne henüz geçilmemiş. GitHub'da 26.000+ star, büyük şirketler aktif olarak kullanıyor. Bu proje de (ilkkod.com müşteri portalı) production'da Drizzle kullanıyor.
Prisma'dan Drizzle'a geçiş zor mu?
Şema tanımı farklı (decorator-based vs fonksiyon-based). Sorgular da farklı. Küçük-orta projeler için birkaç günlük geçiş süresi bekleyin. Ancak "mükemmel geçiş rehberi" aramanıza gerek yok — Drizzle dokümanlarında migration guide mevcut.
Neon dışında hangi PostgreSQL sağlayıcıları destekleniyor?
Tüm standart PostgreSQL sağlayıcıları: Supabase, Railway, Render, PlanetScale (MySQL için), local PostgreSQL. Drizzle, connection string aldığı için sağlayıcıdan bağımsız.
SQLite veya MySQL ile kullanabilir miyim?
Evet. dialect: 'sqlite' veya dialect: 'mysql' ile driver değiştirin. Import path'ler de değişir: drizzle-orm/better-sqlite3, drizzle-orm/mysql2, vb.
Server Actions'da transaction kullanabilir miyim?
Evet, ancak HTTP driver yerine WebSocket driver gerekir (veya standart pg paketi). Server Actions Node.js runtime'da çalışır, Edge'de değil — bu nedenle WebSocket driver burada kullanılabilir.
drizzle-kit studio production veritabanına bağlanabilir mi?
Evet. DATABASE_URL değerini production connection string olarak ayarlayıp bunx drizzle-kit studio çalıştırırsanız production DB'yi görebilirsiniz. Dikkat: Production veritabanında dikkatli olun; yanlışlıkla veri silme riski var.
Sonuç
Drizzle ORM, Next.js ekosistemi için güçlü bir seçim. TypeScript-first tasarımı, küçük bundle boyutu ve Edge uyumluluğu onu serverless ortamlar için ideal kılıyor. Prisma'dan daha düşük seviyeli SQL kontrolü sunarken, Kysely gibi salt query builder'lardan daha yüksek seviyede bir developer experience sağlıyor.
Yeni bir Next.js projesi başlatıyorsanız — özellikle Vercel veya Neon kullanacaksanız — Drizzle'ı tercih etmenizi öneririm. Öğrenme eğrisi mevcut, ama SQL bilen biri için birkaç saatte kavranıyor.
Kurumsal web tasarımı veya e-ticaret projenizde modern bir stack kullanmak istiyorsanız proje teklif formumuzu doldurabilirsiniz. Next.js 16, Drizzle ORM ve Neon PostgreSQL ile performanslı, ölçeklenebilir çözümler geliştiriyoruz.

