Isaac.

Drizzle ORM — A Practical Guide

A concise, friendly walkthrough for getting productive with Drizzle in TypeScript.

What is Drizzle ORM?

Drizzle is a type-safe ORM built for TypeScript-first teams. It combines a SQL-like API with excellent TypeScript inference, small runtime overhead, and a migration system that keeps schema changes pleasant.

Focus areas include type safety, familiar SQL-like syntax, performance, and migrations.

Key Features

1. Type Safety

Drizzle infers types from your table schema so your queries stay typed end-to-end.

<span style="color:#7dd3fc">import</span> <span style="color:#c792ea">{ pgTable, serial, text, varchar }</span> <span style="color:#7dd3fc">from</span> <span style="color:#8bd5a6">'drizzle-orm/pg-core'</span>;

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: varchar('email', { length: 255 }).notNull().unique(),
});

<span style="color:#ffcb6b">type</span> User = typeof users.$inferSelect; // { id: number, name: string, email: string }
<span style="color:#ffcb6b">type</span> NewUser = typeof users.$inferInsert; // { name: string, email: string }

2. SQL-like Syntax

Queries feel familiar if you know SQL, with helper functions for joins, filters, and aggregates.

const result = await db
  .select()
  .from(users)
  .where(eq(users.email, 'user@example.com'))
  .limit(1);

const userPosts = await db
  .select({ userName: users.name, postTitle: posts.title })
  .from(users)
  .innerJoin(posts, eq(users.id, posts.authorId));

3. Database Support

  • PostgreSQL (most feature-complete)
  • MySQL
  • SQLite
  • Cloudflare D1 (SQLite)

Basic Usage

Installation

npm install drizzle-orm
npm install -D drizzle-kit

Setup

A concise schema file and a drizzle connection are all you need to start.

// db/schema.ts
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').notNull().unique(),
  createdAt: timestamp('created_at').defaultNow(),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
});

Database Connection

// db/index.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';

const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
export const db = drizzle(pool, { schema });

CRUD — Example

Here are common create/read/update/delete operations.

// Create
const newUser = await db.insert(users).values({ name: 'John Doe', email: 'john@example.com' }).returning();

// Read
const allUsers = await db.select().from(users);
const user = await db.select().from(users).where(eq(users.id, 1));

// Update
await db.update(users).set({ name: 'Jane Doe' }).where(eq(users.id, 1));

// Delete
await db.delete(users).where(eq(users.id, 1));

Migrations

Drizzle ships with a CLI (drizzle-kit) for generation and a migrator for applying changes.

Config

{
  "out": "./drizzle",
  "schema": "./db/schema.ts",
  "breakpoints": true
}

Commands

npx drizzle-kit generate:pg

import { migrate } from 'drizzle-orm/node-postgres/migrator';
await migrate(db, { migrationsFolder: './drizzle' });

Advanced Features

Relations

import { relations } from 'drizzle-orm';

export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts) }));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));

Transactions

await db.transaction(async (tx) => {
  const user = await tx.insert(users).values({ name: 'Alice', email: 'alice@example.com' }).returning();
  await tx.insert(posts).values({ title: 'First Post', authorId: user[0].id });
});

Aggregations

const userStats = await db
  .select({ userId: users.id, postCount: count(posts.id), latestPost: max(posts.createdAt) })
  .from(users)
  .leftJoin(posts, eq(users.id, posts.authorId))
  .groupBy(users.id);

Comparison & When to Use

Drizzle sits between low-level query builders and full "magic" ORMs. Here are practical tradeoffs.

FeatureDrizzlePrismaTypeORM
Type Safety★★★★★★★★★★★★★
Performance★★★★★★★★★★★
Learning Curve★★★★★★★★★★★

When to Use Drizzle

  • TypeScript projects that value explicit typing.
  • Teams comfortable with SQL.
  • Performance-sensitive services.

When to Consider Alternatives

  • Beginners who want a highly abstracted API.
  • Projects depending on ORMs with broad ecosystem integrations.

Getting Started

Official docs and examples are excellent first stops.

  • Official Docs: orm.drizzle.team
  • GitHub: drizzle-team/drizzle-orm

Drizzle balances SQL familiarity with TypeScript type-safety, making it a great fit for modern apps.