What it is
Drizzle is a TypeScript ORM whose query builder mirrors SQL closely, with no code generation step and a very small runtime suitable for serverless and edge environments.
Define tables as TypeScript objects. The query builder mirrors SQL clauses one-to-one, and drizzle-kit generates migrations from schema changes.
Installation
npm install drizzle-orm && npm install -D drizzle-kitGetting started
The smallest useful thing you can do with it, and what each part means.
import { pgTable, serial, text, integer, timestamp } from 'drizzle-orm/pg-core';
export const books = pgTable('books', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
year: integer('year').notNull(),
authorId: integer('author_id').references(() => authors.id),
createdAt: timestamp('created_at').defaultNow(),
});
// Types come straight from the definition — no generate step.
type Book = typeof books.$inferSelect;
type NewBook = typeof books.$inferInsert;import { eq, and, gte, desc } from 'drizzle-orm';
const recent = await db
.select({ id: books.id, title: books.title, author: authors.name })
.from(books)
.leftJoin(authors, eq(books.authorId, authors.id))
.where(and(gte(books.year, 1990), eq(books.published, true)))
.orderBy(desc(books.year))
.limit(10);Advanced usage
Where the library earns its place over a simpler alternative.
await db.transaction(async (tx) => {
await tx.update(stock)
.set({ quantity: sql`${stock.quantity} - ${qty}` })
.where(eq(stock.sku, sku));
await tx.insert(orders).values({ sku, qty });
});
// Prepared statements are compiled once — valuable on hot paths.
const byYear = db.select().from(books)
.where(gte(books.year, sql.placeholder('year')))
.prepare('books_by_year');
const rows = await byYear.execute({ year: 1990 });Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Types are unexpectedly wide
- Use an explicit select object rather than select() with no argument, so the result type lists only the columns you asked for.
- Migrations do not reflect a schema change
- drizzle-kit reads the schema path from drizzle.config.ts. Confirm the path and re-run generate.
Best practices
- Prefer Drizzle over Prisma when cold start or bundle size matters — serverless and edge in particular.
- Use $inferSelect and $inferInsert rather than hand-writing row types.
- Generate migrations with drizzle-kit and review the SQL before applying.
- Use the sql template tag for expressions the builder cannot represent, never string concatenation.
Background
Why it exists, and what it was reacting to.
Drizzle was built for developers who found Prisma's generated client and Rust engine too heavy — particularly in serverless, where cold starts and bundle size matter. Schemas are plain TypeScript, so types are immediate.
