Back to Knowledge
Updated Sep 2026
PlaybookDatabase

Drizzle ORM

Drizzle

Drizzle ORM

A lightweight, type-safe ORM for TypeScript. Your schema IS your types — no code generation, no sync issues. SQL-like syntax that feels natural.

"Like having a personal translator who speaks both TypeScript and SQL fluently. Zero confusion."

is a TypeScript ORM

ORM (Object-Relational Mapping)

A tool that lets you interact with databases using your programming language instead of raw SQL. Drizzle and Prisma are popular choices.

"Like Google Translate for databases. You speak JavaScript, it translates to SQL."

that gives you type-safe database queries. Think of it as a translator between your TypeScript code and SQL

SQL (Structured Query Language)

The standard language for talking to relational databases, where data lives in tables with rows and columns. You use it to create, read, update, and delete data ("give me all users where age > 21"). PostgreSQL, MySQL, and SQLite all speak SQL; ORMs like Drizzle write it for you.

"Like asking a very literal librarian for books from a perfectly organized spreadsheet. Precise question, precise answer."

: write queries in code, get autocomplete, and catch mistakes before they hit production.

Why Drizzle?

Raw SQL strings are error-prone. Heavy ORMs hide what's happening. Drizzle is the best of both worlds, and AI assistants write it fluently because it reads like SQL:

Type Safety

TypeScript knows your schema. Typo in a column name? Red squiggle before you run.

SQL-Like Syntax

Reads like SQL, so you (and your reviewer) understand what's actually running.

Lightweight

No heavy runtime or codegen step. Fast cold starts, which suits serverless functions.

Migrations Built-In

Schema changes are tracked and applied with drizzle-kit.

Versions as of Sep 2026: drizzle-orm 0.45 and drizzle-kit 0.31 are the stable releases; 1.0 is in beta. Everything on this page uses the stable APIs.

Install and Configure

Terminal

npm install drizzle-orm @neondatabase/serverless dotenv
npm install -D drizzle-kit

drizzle.config.ts

import { defineConfig } from "drizzle-kit";
import { config } from "dotenv";

config({ path: ".env.local" });

export default defineConfig({
  schema: "./lib/db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: { url: process.env.DATABASE_URL! },
});

lib/db/index.ts

import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";

const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle({ client: sql });

The step-by-step version (with env vars and deploy) is in Connect a Database.

Defining Your Schema

Your schema

Schema

The structure of your database — what tables exist, what columns they have, and how they relate to each other.

"Like the blueprint of a building. It defines the shape before you add the furniture (data)."

lives in a TypeScript file, usually lib/db/schema.ts. Define tables with Drizzle's helpers:

lib/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"),
  userId: integer("user_id").references(() => users.id),
  createdAt: timestamp("created_at").defaultNow(),
});

SaucyTech Stack

We use Neon Postgres, so use pgTable from drizzle-orm/pg-core. For MySQL or SQLite (Turso), import from drizzle-orm/mysql-core or drizzle-orm/sqlite-core. Designing tables from scratch? Read Schema Design first.

CRUD Operations

Create (Insert)

// Insert one user and get it back
const [newUser] = await db
  .insert(users)
  .values({ name: "Alice", email: "alice@example.com" })
  .returning();

// Insert multiple
await db.insert(users).values([
  { name: "Bob", email: "bob@example.com" },
  { name: "Carol", email: "carol@example.com" },
]);

Read (Select)

import { eq, desc } from "drizzle-orm";

// Get all users
const allUsers = await db.select().from(users);

// Get one by ID
const user = await db.select().from(users).where(eq(users.id, 1));

// Get specific columns
const names = await db
  .select({ name: users.name, email: users.email })
  .from(users);

// With ordering and limit
const recent = await db
  .select()
  .from(users)
  .orderBy(desc(users.createdAt))
  .limit(10);

Update

// Update a user
await db.update(users).set({ name: "Alice Smith" }).where(eq(users.id, 1));

// Update and return the new row
const [updated] = await db
  .update(users)
  .set({ name: "Alice Smith" })
  .where(eq(users.id, 1))
  .returning();

Delete

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

// Delete and get the deleted rows back
const deleted = await db.delete(users).where(eq(users.id, 1)).returning();

Warning

Always include a .where() clause! Without it, you'll update or delete every row. Watch for this in AI-generated code.

Joins & Relations

// Inner join: posts with their authors
const postsWithAuthors = await db
  .select({ postTitle: posts.title, authorName: users.name })
  .from(posts)
  .innerJoin(users, eq(posts.userId, users.id));

// Left join: all users, with their posts (if any)
const usersWithPosts = await db
  .select()
  .from(users)
  .leftJoin(posts, eq(users.id, posts.userId));

Drizzle Relations (Optional)

For nested results (a user with an array of posts), define relations and use db.query. The relations API is being reworked for Drizzle 1.0, so check the docs for the version you installed.

Migrations

Migration

A controlled change to your database schema. Lets you version-control your database structure and safely update it.

"Like renovating a house room by room, with blueprints for each change."

with drizzle-kit

There are two workflows. Use push while prototyping and generate + migrate once real data exists.

1

Generate a migration

After changing your schema, write the SQL diff to the drizzle/ folder:

Terminal

npx drizzle-kit generate
2

Review the SQL

Open the new file and read it. Look for DROP statements and renamed columns that became drop-and-add. Commit it with your code.

3

Apply it

Run pending migrations against the database (try a Neon dev branch first):

Terminal

npx drizzle-kit migrate

Quick Sync & Friends

npx drizzle-kit push syncs your schema directly with no migration files (great for early iteration, risky on production). npx drizzle-kit pull generates a schema from an existing database. npx drizzle-kit studio opens a visual browser.

Common Pitfalls

N+1 Query Problem

Looping through results and running a query for each row = slow.

Fix: Use joins or inArray(). Fetch related data in one query, not N queries.

Forgetting to import operators

eq, desc, and, or must be imported.

Fix: import { eq, desc, and, or, like, inArray } from "drizzle-orm"

Mixing push and migrate on the same database

Push changes the database without writing a migration file, so the migration history no longer matches reality.

Fix: Pick one per environment. Push on throwaway dev branches; generate + migrate for production.

Missing connection pooling

Serverless functions open many connections. Without pooling, you'll hit limits.

Fix: Use the neon-http driver, or the pooled connection string (the one with -pooler in the host) for TCP drivers.

Ready to connect your database?

Now that you know Drizzle, wire it to Neon and design your tables.