Drizzle ORM
Rules
- Schema definition:
pgTable() / mysqlTable() / sqliteTable() in src/db/schema.ts
- Development:
drizzle-kit push for fast iteration — no migration files needed
- Production:
drizzle-kit generate + drizzle-kit migrate for trackable migration history
- Type-safe queries:
db.select().from(users).where(eq(users.email, email)) — full TypeScript inference
- Relations: define with
relations() helper — one() and many() for relational queries with db.query
- Prepared statements:
db.select().from(users).where(eq(users.id, sql.placeholder("id"))).prepare() — reuse for hot paths
- Transactions:
db.transaction(async (tx) => { ... }) — use tx not db inside the callback
- Indexes: define inline with
.index() or in table config — add on columns used in where and orderBy
- Drizzle Studio:
npx drizzle-kit studio for visual database browser during development
Patterns
import { pgTable, text, timestamp, uuid, integer } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export const posts = pgTable("posts", {
id: uuid("id").primaryKey().defaultRandom(),
title: text("title").notNull(),
authorId: uuid("author_id").references(() => users.id).notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
const result = await db.query.users.findFirst({
where: eq(users.id, userId),
with: { posts: true },
});
Avoid
drizzle-kit push in production — use generate + migrate for tracked migrations
- Raw SQL when Drizzle API covers it — use
sql template tag only for complex expressions
- Forgetting
relations() definitions — without them, db.query relational queries won't work
- N+1 queries — use
with in db.query to join relations in a single query
- Missing indexes on foreign keys and filtered columns — Drizzle doesn't auto-create them