# Drizzle ORM: Die leichtgewichtige Prisma-Alternative **Meta-Description:** Drizzle ORM für TypeScript. SQL-nah, serverless-optimiert und nur 7KB. Vergleich mit Prisma und Migration Guide. **Keywords:** Drizzle ORM, TypeScript ORM, SQL Builder, Serverless Database, Prisma Alternative, Edge Computing, Lightweight ORM --- ## Einführung Drizzle ORM ist ein **TypeScript-first ORM** mit SQL-naher Syntax. Mit nur ~7KB (gzipped) und null Dependencies ist es perfekt für Serverless und Edge Computing – wo Prismas Bundle-Size zum Problem wird. --- ## Drizzle vs Prisma ``` ┌─────────────────────────────────────────────────────────────┐ │ DRIZZLE vs PRISMA │ ├─────────────────────────────────────────────────────────────┤ │ │ │ DRIZZLE PRISMA │ │ ──────────────────── ──────────────────── │ │ ~7KB gzipped ~600KB+ (mit Engine) │ │ SQL-nah (Query Builder) Abstrahierte API │ │ Code-First Schema Schema-First (.prisma) │ │ Zero Dependencies Rust Binary Engine │ │ Instant Cold Starts Cold Start Issues │ │ │ │ Best for: Best for: │ │ • Serverless/Edge • Rapid Development │ │ • SQL-Erfahrene Devs • Schema-First Teams │ │ • Performance-Critical • Große Teams/Abstraction │ │ • Kleine Bundles • Prisma Studio │ │ │ │ Supported DBs: Supported DBs: │ │ PostgreSQL, MySQL, SQLite PostgreSQL, MySQL, │ │ Turso, Neon, PlanetScale SQLite, MongoDB, │ │ Cloudflare D1, Vercel SQL Server, CockroachDB │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Installation & Setup ```bash # Installation npm install drizzle-orm npm install -D drizzle-kit # Für PostgreSQL npm install postgres # Für MySQL npm install mysql2 # Für SQLite/Turso npm install @libsql/client ``` ```typescript // drizzle.config.ts import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/db/schema.ts', out: './drizzle', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL! } }); ``` --- ## Schema Definition (Code-First) ```typescript // src/db/schema.ts import { pgTable, uuid, text, timestamp, boolean, integer, pgEnum } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; // Enum export const roleEnum = pgEnum('role', ['user', 'admin', 'moderator']); // Users Table export const users = pgTable('users', { id: uuid('id').defaultRandom().primaryKey(), email: text('email').notNull().unique(), name: text('name'), role: roleEnum('role').default('user').notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull() }); // Posts Table export const posts = pgTable('posts', { id: uuid('id').defaultRandom().primaryKey(), title: text('title').notNull(), content: text('content'), published: boolean('published').default(false).notNull(), authorId: uuid('author_id') .notNull() .references(() => users.id, { onDelete: 'cascade' }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull() }); // Comments Table export const comments = pgTable('comments', { id: uuid('id').defaultRandom().primaryKey(), content: text('content').notNull(), postId: uuid('post_id') .notNull() .references(() => posts.id, { onDelete: 'cascade' }), authorId: uuid('author_id') .notNull() .references(() => users.id, { onDelete: 'cascade' }), createdAt: timestamp('created_at').defaultNow().notNull() }); // Relations export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts), comments: many(comments) })); 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] }) })); // Type Inference export type User = typeof users.$inferSelect; export type NewUser = typeof users.$inferInsert; export type Post = typeof posts.$inferSelect; export type NewPost = typeof posts.$inferInsert; ``` --- ## Database Connection ```typescript // src/db/index.ts import { drizzle } from 'drizzle-orm/postgres-js'; import postgres from 'postgres'; import * as schema from './schema'; const connectionString = process.env.DATABASE_URL!; // Für Queries const queryClient = postgres(connectionString); export const db = drizzle(queryClient, { schema }); // Für Migrations (separater Client) const migrationClient = postgres(connectionString, { max: 1 }); export const migrationDb = drizzle(migrationClient); ``` ```typescript // Für Serverless (Neon, Vercel) import { drizzle } from 'drizzle-orm/neon-http'; import { neon } from '@neondatabase/serverless'; import * as schema from './schema'; const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql, { schema }); ``` ```typescript // Für Edge (Cloudflare D1) import { drizzle } from 'drizzle-orm/d1'; import * as schema from './schema'; export interface Env { DB: D1Database; } export default { async fetch(request: Request, env: Env) { const db = drizzle(env.DB, { schema }); // ... } }; ``` --- ## CRUD Operations ```typescript import { db } from '@/db'; import { users, posts, comments } from '@/db/schema'; import { eq, and, or, desc, asc, like, sql } from 'drizzle-orm'; // CREATE async function createUser(data: NewUser) { const [user] = await db .insert(users) .values(data) .returning(); return user; } async function createManyUsers(data: NewUser[]) { return await db .insert(users) .values(data) .returning(); } // READ - Single async function getUserById(id: string) { const [user] = await db .select() .from(users) .where(eq(users.id, id)) .limit(1); return user; } // READ - Mit Relations (Query API) async function getUserWithPosts(id: string) { const result = await db.query.users.findFirst({ where: eq(users.id, id), with: { posts: { where: eq(posts.published, true), orderBy: [desc(posts.createdAt)], limit: 10 } } }); return result; } // READ - Liste mit Filtering async function getPublishedPosts(page: number = 1, pageSize: number = 10) { const offset = (page - 1) * pageSize; const results = await db .select({ id: posts.id, title: posts.title, content: posts.content, createdAt: posts.createdAt, authorName: users.name, authorEmail: users.email }) .from(posts) .innerJoin(users, eq(posts.authorId, users.id)) .where(eq(posts.published, true)) .orderBy(desc(posts.createdAt)) .limit(pageSize) .offset(offset); return results; } // UPDATE async function updatePost(id: string, data: Partial) { const [updated] = await db .update(posts) .set({ ...data, updatedAt: new Date() }) .where(eq(posts.id, id)) .returning(); return updated; } // UPSERT async function upsertUser(email: string, data: Partial) { const [user] = await db .insert(users) .values({ email, ...data }) .onConflictDoUpdate({ target: users.email, set: { ...data, updatedAt: new Date() } }) .returning(); return user; } // DELETE async function deletePost(id: string) { const [deleted] = await db .delete(posts) .where(eq(posts.id, id)) .returning(); return deleted; } ``` --- ## Advanced Queries ```typescript import { sql, count, avg, sum } from 'drizzle-orm'; // Aggregations async function getPostStats() { const [stats] = await db .select({ totalPosts: count(posts.id), publishedPosts: count(sql`CASE WHEN ${posts.published} THEN 1 END`), }) .from(posts); return stats; } // Group By async function getPostsByAuthor() { return await db .select({ authorId: posts.authorId, authorName: users.name, postCount: count(posts.id) }) .from(posts) .innerJoin(users, eq(posts.authorId, users.id)) .groupBy(posts.authorId, users.name) .orderBy(desc(count(posts.id))); } // Subqueries async function getUsersWithPostCount() { const postCountSubquery = db .select({ authorId: posts.authorId, count: count(posts.id).as('post_count') }) .from(posts) .groupBy(posts.authorId) .as('post_counts'); return await db .select({ id: users.id, name: users.name, postCount: postCountSubquery.count }) .from(users) .leftJoin(postCountSubquery, eq(users.id, postCountSubquery.authorId)); } // Raw SQL async function searchPosts(query: string) { return await db.execute(sql` SELECT * FROM posts WHERE to_tsvector('german', title || ' ' || content) @@ plainto_tsquery('german', ${query}) `); } // Transactions async function createPostWithComments( post: NewPost, comments: { content: string; authorId: string }[] ) { return await db.transaction(async (tx) => { const [newPost] = await tx .insert(posts) .values(post) .returning(); if (comments.length > 0) { await tx.insert(comments).values( comments.map(c => ({ ...c, postId: newPost.id })) ); } return newPost; }); } ``` --- ## Migrations ```bash # Schema-Änderungen generieren npx drizzle-kit generate # Migrations ausführen npx drizzle-kit migrate # Push (Development - ohne Migration Files) npx drizzle-kit push # Studio (GUI) npx drizzle-kit studio ``` ```typescript // Programmatische Migration import { migrate } from 'drizzle-orm/postgres-js/migrator'; import { migrationDb } from './db'; async function runMigrations() { await migrate(migrationDb, { migrationsFolder: './drizzle' }); console.log('Migrations complete'); } ``` --- ## Zod Integration ```typescript import { createInsertSchema, createSelectSchema } from 'drizzle-zod'; import { z } from 'zod'; import { users, posts } from './schema'; // Auto-generierte Schemas const insertUserSchema = createInsertSchema(users); const selectUserSchema = createSelectSchema(users); // Mit Erweiterungen const createUserSchema = createInsertSchema(users, { email: z.string().email('Ungültige E-Mail'), name: z.string().min(2, 'Name zu kurz').optional() }).omit({ id: true, createdAt: true, updatedAt: true }); const updateUserSchema = createUserSchema.partial(); // Verwendung async function createUserValidated(input: unknown) { const data = createUserSchema.parse(input); return await createUser(data); } ``` --- ## Fazit Drizzle ORM bietet: 1. **Minimal Footprint**: ~7KB, zero dependencies 2. **SQL-Kontrolle**: Transparente, vorhersagbare Queries 3. **Type-Safety**: Volle TypeScript-Integration 4. **Edge-Ready**: Perfekt für Serverless/Edge Für Performance-kritische und serverless Anwendungen ist Drizzle die bessere Wahl. --- ## Bildprompts 1. "Lightweight feather next to database icons, minimal bundle size concept" 2. "SQL code transforming into TypeScript types, type-safe ORM visualization" 3. "Edge computing nodes with database connections, serverless architecture" --- ## Quellen - [Drizzle ORM Documentation](https://orm.drizzle.team/) - [Drizzle vs Prisma Comparison](https://www.prisma.io/docs/orm/more/comparisons/prisma-and-drizzle) - [Drizzle vs Prisma 2026](https://medium.com/@codabu/drizzle-vs-prisma-choosing-the-right-typescript-orm-in-2026-deep-dive-63abb6aa882b) - [Drizzle Kit](https://orm.drizzle.team/kit-docs/overview)