Yeni·Müşteri Portalı Açıldı — Projelerinizi, Faturalarınızı ve Destek Taleplerinizi YönetinKampanya·Ücretsiz Web Sitesi Değerlendirmesi — Sitenizin Eksiklerini Hemen ÖğreninBlog·2026'da Kurumsal Web Sitesi Yaptırma: Fiyatlar, Süreç ve Doğru Ajans Seçimiİçgörü·Müşteri Projelerinde Dönüşüm Oranı Ortalama %40 Artış SağlandıGüncelleme·Yeni: Proje İlerlemesini Anlık Takip Et, Faturalara Ulaş, Destek Talebi AçBlog·Yapay Zeka ile Web Tasarım: 2026 Rehberi — Neler Değişiyor?Kampanya·Projenizi Başlatmak İçin Ücretsiz Keşif Görüşmesi — Bugün Randevu Alİçgörü·Ortalama Proje Teslim Süresi: 14 Gün — GarantiliBlog·Vibe Coding ile Gerçek Proje Geliştirme: Türk Geliştirici İçin Pratik RehberYeni·Sektöre Özel Web Çözümleri — Emlak, Otel, Restoran, Klinik ve Daha FazlasıGüncelleme·Tüm Projeler İçin Ücretsiz SSL, CDN ve Hız Optimizasyonu DahilKampanya·Fiyat Hesaplama Aracı — Projenizin Maliyetini 2 Dakikada ÖğreninBlog·Türkiye'de Micro-SaaS Kurma: Next.js + PayTR ile Aylık Gelir ModeliHaber·Ankara'da Yeni Çözüm Ortaklıkları ile Hizmet Ağı GenişliyorYeni·Yeni: Haberler Sayfası Açıldı — Sektör Gelişmeleri ve Teknoloji GüncellemeleriYeni·Müşteri Portalı Açıldı — Projelerinizi, Faturalarınızı ve Destek Taleplerinizi YönetinKampanya·Ücretsiz Web Sitesi Değerlendirmesi — Sitenizin Eksiklerini Hemen ÖğreninBlog·2026'da Kurumsal Web Sitesi Yaptırma: Fiyatlar, Süreç ve Doğru Ajans Seçimiİçgörü·Müşteri Projelerinde Dönüşüm Oranı Ortalama %40 Artış SağlandıGüncelleme·Yeni: Proje İlerlemesini Anlık Takip Et, Faturalara Ulaş, Destek Talebi AçBlog·Yapay Zeka ile Web Tasarım: 2026 Rehberi — Neler Değişiyor?Kampanya·Projenizi Başlatmak İçin Ücretsiz Keşif Görüşmesi — Bugün Randevu Alİçgörü·Ortalama Proje Teslim Süresi: 14 Gün — GarantiliBlog·Vibe Coding ile Gerçek Proje Geliştirme: Türk Geliştirici İçin Pratik RehberYeni·Sektöre Özel Web Çözümleri — Emlak, Otel, Restoran, Klinik ve Daha FazlasıGüncelleme·Tüm Projeler İçin Ücretsiz SSL, CDN ve Hız Optimizasyonu DahilKampanya·Fiyat Hesaplama Aracı — Projenizin Maliyetini 2 Dakikada ÖğreninBlog·Türkiye'de Micro-SaaS Kurma: Next.js + PayTR ile Aylık Gelir ModeliHaber·Ankara'da Yeni Çözüm Ortaklıkları ile Hizmet Ağı GenişliyorYeni·Yeni: Haberler Sayfası Açıldı — Sektör Gelişmeleri ve Teknoloji Güncellemeleri
ilkkod
Next.js Entegrasyonları

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.

İlker
25 Mart 2026
20 dk
Next.js ile Drizzle ORM + PostgreSQL: Prisma'dan Daha İyi mi?

Ö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)

ORMBundle SizeTypeScriptEdge UyumluSQL 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-orm v1.0.0-beta.18, drizzle-kit 0.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:

KomutNe YaparNe Zaman
bunx drizzle-kit pushSchema'yı direkt DB'ye uygular, migration dosyası oluşturmazGeliştirme / prototipleme
bunx drizzle-kit generateSQL migration dosyaları oluşturur (drizzle/ klasörüne)Her schema değişikliğinde
bunx drizzle-kit migrategenerate ile oluşturulan SQL'leri DB'ye uygularProduction 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 deployDrizzle
Edge Functions / Cloudflare WorkersDrizzle
Büyük, legacy enterprise projePrisma (mevcut altyapı)
Karmaşık SQL sorguları çok fazlaDrizzle
Takım Prisma deneyimliPrisma (öğrenme eğrisi)
Serverless, soğuk başlatma kritikDrizzle

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())
  • createdAt ve updatedAt alanları var, .defaultNow() ayarlı
  • UUID veya serial primary key seçimi bilinçli yapıldı

Migration

  • drizzle-kit push yerine generate + migrate akışı kullanılıyor
  • Migration SQL dosyaları Git'e commit edildi
  • Production'da migration deploy script'e eklendi

Bağlantı

  • DATABASE_URL environment 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)
  • sql template 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 limit var
  • Promise.all ile 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.

Paylaş: