What is Drizzle ORM?
Drizzle ORM is a TypeScript ORM that embraces SQL instead of hiding it. Unlike Prisma that has its own query language, Drizzle's query builder maps 1:1 to SQL — if you know SQL, you already know Drizzle.
Why Drizzle Over Prisma?
- Zero overhead — no query engine binary, no runtime code generation
- SQL-like syntax — query builder mirrors SQL exactly
- Serverless-ready — works with edge runtimes (no binary dependency)
- Multiple drivers — PostgreSQL, MySQL, SQLite, Neon, Turso, PlanetScale
- Drizzle Kit — automatic migration generation from schema changes
- Type-safe — infers types from schema, no manual type definitions
Quick Start
npm install drizzle-orm postgres
npm install -D drizzle-kit
Define Schema
// schema.ts
import { pgTable, serial, text, timestamp, boolean, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').unique().notNull(),
role: text('role', { enum: ['admin', 'user'] }).default('user'),
createdAt: timestamp('created_at').defaultNow()
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false),
authorId: integer('author_id').references(() => users.id),
createdAt: timestamp('created_at').defaultNow()
});
Query Builder (SQL-like)
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { users, posts } from './schema';
import { eq, and, gt, desc, count } from 'drizzle-orm';
const client = postgres('postgresql://...');
const db = drizzle(client);
// SELECT * FROM users WHERE role = 'admin'
const admins = await db.select().from(users).where(eq(users.role, 'admin'));
// INSERT
const newUser = await db.insert(users).values({
name: 'Alex', email: 'alex@example.com'
}).returning();
// JOIN
const postsWithAuthors = await db
.select({
postTitle: posts.title,
authorName: users.name
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt))
.limit(10);
// Aggregation
const postCounts = await db
.select({
authorId: posts.authorId,
postCount: count(posts.id)
})
.from(posts)
.groupBy(posts.authorId);
Relational Queries (Prisma-like)
import * as schema from './schema';
const db = drizzle(client, { schema });
// Prisma-like nested queries
const usersWithPosts = await db.query.users.findMany({
with: {
posts: {
where: eq(posts.published, true),
limit: 5,
orderBy: desc(posts.createdAt)
}
},
where: eq(users.role, 'admin')
});
Migrations
// drizzle.config.ts
export default {
schema: './schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: { url: process.env.DATABASE_URL! }
};
# Generate migration from schema changes
npx drizzle-kit generate
# Apply migrations
npx drizzle-kit migrate
# Studio (visual database browser)
npx drizzle-kit studio
Drizzle vs Alternatives
| Feature | Drizzle | Prisma | TypeORM | Kysely |
|---|---|---|---|---|
| Bundle size | ~30KB | 8MB+ engine | 500KB+ | ~50KB |
| Edge/serverless | Yes | Accelerate needed | No | Yes |
| SQL-like syntax | Yes | Custom language | Partial | Yes |
| Relational queries | Both styles | findMany | Relations | SQL only |
| Migrations | Auto-generated | Auto-generated | Manual | Manual |
| Learning curve | Low (know SQL) | Medium | High | Low |
Real-World Impact
A SaaS on Vercel Edge Functions used Prisma. Cold starts: 3-5 seconds (Prisma engine binary). After migrating to Drizzle: cold starts dropped to 200ms. Same queries, same database, same hosting — just no 8MB binary to load. Users stopped seeing loading spinners.
Building TypeScript backends? I help teams choose the right ORM and database stack. Contact spinov001@gmail.com or explore my data tools on Apify.
Top comments (0)