| name | drizzle-orm |
| description | Type-safe SQL ORM for TypeScript with zero runtime overhead |
| user-invocable | false |
| disable-model-invocation | true |
| progressive_disclosure | {"entry_point":{"summary":"Type-safe SQL ORM for TypeScript with zero runtime overhead","when_to_use":"When working with drizzle-orm or related functionality.","quick_start":"1. Review the core concepts below. 2. Apply patterns to your use case. 3. Follow best practices for implementation."},"references":["advanced-schemas.md","performance.md","query-patterns.md","vs-prisma.md"]} |
Drizzle ORM
Modern TypeScript-first ORM with zero dependencies, compile-time type safety, and SQL-like syntax. Optimized for edge runtimes and serverless environments.
Quick Start
Installation
npm install drizzle-orm
npm install pg
npm install mysql2
npm install better-sqlite3
npm install -D drizzle-kit
Basic Setup
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
createdAt: timestamp("created_at").defaultNow(),
});
import { drizzle } from "drizzle-orm/node-postgres";
import * as schema from "./schema";
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle(pool, { schema });
First Query
import { users } from "./db/schema";
import { eq } from "drizzle-orm";
import { db } from "./db/client";
const newUser = await db
.insert(users)
.values({
email: "user@example.com",
name: "John Doe",
})
.returning();
const allUsers = await db.select().from(users);
const user = await db.select().from(users).where(eq(users.id, 1));
await db.update(users).set({ name: "Jane Doe" }).where(eq(users.id, 1));
await db.delete(users).where(eq(users.id, 1));
Schema Definition
Column Types Reference
| PostgreSQL | MySQL | SQLite | TypeScript |
|---|
serial() | serial() | integer() | number |
text() | text() | text() | string |
integer() | int() | integer() | number |
boolean() | boolean() | integer() | boolean |
timestamp() | datetime() | integer() | Date |
json() | json() | text() | unknown |
uuid() | varchar(36) | text() | string |
Common Schema Patterns
import { pgTable, serial, text, varchar, integer, boolean, timestamp, json, unique } from "drizzle-orm/pg-core";
export const users = pgTable(
"users",
{
id: serial("id").primaryKey(),
email: varchar("email", { length: 255 }).notNull().unique(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
role: text("role", { enum: ["admin", "user", "guest"] }).default("user"),
metadata: json("metadata").$type<{ theme: string; locale: string }>(),
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
},
(table) => ({
emailIdx: unique("email_unique_idx").on(table.email),
}),
);
type User = typeof users.$inferSelect;
type NewUser = typeof users.$inferInsert;
Relations
One-to-Many
import { pgTable, serial, text, integer } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
export const authors = pgTable("authors", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
authorId: integer("author_id")
.notNull()
.references(() => authors.id),
});
export const authorsRelations = relations(authors, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(authors, {
fields: [posts.authorId],
references: [authors.id],
}),
}));
const authorsWithPosts = await db.query.authors.findMany({
with: { posts: true },
});
Many-to-Many
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
});
export const groups = pgTable("groups", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
});
export const usersToGroups = pgTable(
"users_to_groups",
{
userId: integer("user_id")
.notNull()
.references(() => users.id),
groupId: integer("group_id")
.notNull()
.references(() => groups.id),
},
(table) => ({
pk: primaryKey({ columns: [table.userId, table.groupId] }),
}),
);
export const usersRelations = relations(users, ({ many }) => ({
groups: many(usersToGroups),
}));
export const groupsRelations = relations(groups, ({ many }) => ({
users: many(usersToGroups),
}));
export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
user: one(users, { fields: [usersToGroups.userId], references: [users.id] }),
group: one(groups, { fields: [usersToGroups.groupId], references: [groups.id] }),
}));
Queries
Filtering
import { eq, ne, gt, gte, lt, lte, like, ilike, inArray, isNull, isNotNull, and, or, between } from "drizzle-orm";
await db.select().from(users).where(eq(users.email, "user@example.com"));
await db.select().from(users).where(gt(users.id, 10));
await db.select().from(users).where(like(users.name, "%John%"));
await db
.select()
.from(users)
.where(and(eq(users.role, "admin"), gt(users.createdAt, new Date("2024-01-01"))));
await db
.select()
.from(users)
.where(inArray(users.id, [1, 2, 3]));
await db.select().from(users).where(isNull(users.deletedAt));
Joins
import { eq } from "drizzle-orm";
const result = await db
.select({
user: users,
post: posts,
})
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId));
const result = await db
.select({
user: users,
post: posts,
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId));
import { count, sql } from "drizzle-orm";
const result = await db
.select({
authorName: authors.name,
postCount: count(posts.id),
})
.from(authors)
.leftJoin(posts, eq(authors.id, posts.authorId))
.groupBy(authors.id);
Pagination & Sorting
import { desc, asc } from "drizzle-orm";
await db.select().from(users).orderBy(desc(users.createdAt));
await db.select().from(users).limit(10).offset(20);
function paginate(page: number, pageSize: number = 10) {
return db
.select()
.from(users)
.limit(pageSize)
.offset(page * pageSize);
}
Transactions
await db.transaction(async (tx) => {
await tx.insert(users).values({ email: 'user@example.com', name: 'John' });
await tx.insert(posts).values({ title: 'First Post', authorId: 1 });
});
const tx = db.transaction(async (tx) => {
const user = await tx.insert(users).values({ ... }).returning();
if (!user) {
tx.rollback();
return;
}
await tx.insert(posts).values({ authorId: user.id });
});
Migrations
Drizzle Kit Configuration
import type { Config } from "drizzle-kit";
export default {
schema: "./db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
} satisfies Config;
Migration Workflow
npx drizzle-kit generate
cat drizzle/0000_migration.sql
npx drizzle-kit migrate
npx drizzle-kit introspect
npx drizzle-kit studio
Example Migration
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"email" varchar(255) NOT NULL,
"name" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "users_email_unique" UNIQUE("email")
);
Navigation
Detailed References
-
🏗️ Advanced Schemas - Custom types, composite keys, indexes, constraints, multi-tenant patterns. Load when designing complex database schemas.
-
🔍 Query Patterns - Subqueries, CTEs, raw SQL, prepared statements, batch operations. Load when optimizing queries or handling complex filtering.
-
⚡ Performance - Connection pooling, query optimization, N+1 prevention, prepared statements, edge runtime integration. Load when scaling or optimizing database performance.
-
🔄 vs Prisma - Feature comparison, migration guide, when to choose Drizzle over Prisma. Load when evaluating ORMs or migrating from Prisma.
Red Flags
Stop and reconsider if:
- Using
any or unknown for JSON columns without type annotation
- Building raw SQL strings without using
sql template (SQL injection risk)
- Not using transactions for multi-step data modifications
- Fetching all rows without pagination in production queries
- Missing indexes on foreign keys or frequently queried columns
- Using
select() without specifying columns for large tables
Performance Benefits vs Prisma
| Metric | Drizzle | Prisma |
|---|
| Bundle Size | ~35KB | ~230KB |
| Cold Start | ~10ms | ~250ms |
| Query Speed | Baseline | ~2-3x slower |
| Memory | ~10MB | ~50MB |
| Type Generation | Runtime inference | Build-time generation |
Integration
- typescript-core: Type-safe schema inference with
satisfies
- nextjs-core: Server Actions, Route Handlers, Middleware integration
- Database Migration: Safe schema evolution patterns
Related Skills
When using Drizzle, these skills enhance your workflow:
- prisma: Alternative ORM comparison: Drizzle vs Prisma trade-offs
- typescript: Advanced TypeScript patterns for type-safe queries
- nextjs: Drizzle with Next.js Server Actions and API routes
- sqlalchemy: SQLAlchemy patterns for Python developers learning Drizzle
[Full documentation available in these skills if deployed in your bundle]