1 min read
Web DevelopmentDrizzle ORM: Die leichtgewichtige Prisma-Alternative
Drizzle ORM für TypeScript. SQL-nah, serverless-optimiert und nur 7KB. Vergleich mit Prisma und Migration Guide.
Drizzle ORMTypeScript ORMSQL BuilderServerless DatabasePrisma AlternativeEdge Computing

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
# 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// 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)
// 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
// 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);// 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 });// 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
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<NewPost>) {
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<NewUser>) {
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
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
# 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// 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
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:
- Minimal Footprint: ~7KB, zero dependencies
- SQL-Kontrolle: Transparente, vorhersagbare Queries
- Type-Safety: Volle TypeScript-Integration
- Edge-Ready: Perfekt für Serverless/Edge
Für Performance-kritische und serverless Anwendungen ist Drizzle die bessere Wahl.
Bildprompts
- "Lightweight feather next to database icons, minimal bundle size concept"
- "SQL code transforming into TypeScript types, type-safe ORM visualization"
- "Edge computing nodes with database connections, serverless architecture"