Drizzle ORM was built on a simple premise: if you already know SQL, an ORM shouldn't make you learn a whole new query language on top of it.
Drizzle is a TypeScript ORM that mirrors SQL syntax closely in its query builder, generates types directly from your schema definition with no separate code-generation step, and ships as a genuinely lightweight runtime — no generated client binary, no engine process, just a thin layer over your database driver. Where Prisma abstracts SQL behind its own query language, Drizzle stays close enough to SQL that reading a Drizzle query and predicting the resulting SQL is usually straightforward.
Why Drizzle Matters (and When to Skip It)
Drizzle's types are inferred directly from TypeScript schema definitions at compile time — no generate step, no build artifact to keep in sync, no separate schema file in a different language (Prisma's .prisma files). For teams that want type safety without an extra tool in the pipeline, and who are comfortable thinking in SQL terms, Drizzle removes a layer of abstraction Prisma adds.
Skip Drizzle if your team prefers Prisma's more declarative, less SQL-literal query style, or values Prisma's more mature migration UI (Prisma Studio) and broader long-term ecosystem — Drizzle is younger and its tooling, while solid, has a smaller track record.
Getting Started with Drizzle
Define the schema directly in TypeScript:
import { pgTable, text, uuid } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: uuid("id").defaultRandom().primaryKey(),
email: text("email").notNull().unique(),
});
export const posts = pgTable("posts", {
id: uuid("id").defaultRandom().primaryKey(),
title: text("title").notNull(),
authorId: uuid("author_id").notNull().references(() => users.id),
});
Query with SQL-like syntax:
import { drizzle } from "drizzle-orm/node-postgres";
import { eq } from "drizzle-orm";
const db = drizzle(pool);
const user = await db
.select()
.from(users)
.where(eq(users.email, "suhail@example.com"));
const newUser = await db.insert(users).values({ email: "new@example.com" }).returning();
Core Drizzle Concepts Every Developer Should Know
Relational queries offer a Prisma-like alternative when you want it. For cases where the SQL-builder style feels verbose, Drizzle's relational query API gives you include-style nested fetching:
const usersWithPosts = await db.query.users.findMany({
with: { posts: true },
});
// fully typed: usersWithPosts[0].posts is Post[]
Migrations are generated from schema diffs, similar to Prisma, via drizzle-kit:
npx drizzle-kit generate
npx drizzle-kit migrate
The generated SQL migration files are plain, readable SQL — no abstraction layer obscuring what's actually being run against the database.
Prepared statements are a first-class performance feature. Drizzle lets you prepare a query once and execute it repeatedly with different parameters, meaningfully reducing per-query overhead for hot paths:
const getUserByEmail = db
.select()
.from(users)
.where(eq(users.email, sql.placeholder("email")))
.prepare("getUserByEmail");
const result = await getUserByEmail.execute({ email: "suhail@example.com" });
Zero runtime dependency on a generated binary means Drizzle's bundle size and cold-start footprint stay small — a real advantage in serverless/edge environments where every dependency's size affects cold start time.
Common Drizzle Mistakes and How to Fix Them
Mistake 1: not using the relational query API for genuinely nested fetches. Manually writing multiple .leftJoin() calls for what's really a straightforward "get user with their posts" is more verbose than needed. Fix: reach for db.query.<table>.findMany({ with: {...} }) for standard nested-fetch patterns.
Mistake 2: forgetting drizzle-kit generate after schema changes. Since there's no separate generation step for the client (types come directly from the schema import), it's easy to forget the migration generation step specifically. Fix: treat schema changes and migration generation as one atomic step in your workflow, ideally scripted together.
Mistake 3: mixing raw SQL and the query builder inconsistently across a codebase. Drizzle supports raw sql template literals for cases the builder doesn't cover well, but scattering them without a clear convention makes the codebase harder to follow. Fix: default to the query builder, and document the specific cases where raw SQL is the accepted exception.
When Should You Use Drizzle Instead of Prisma?
Use Drizzle when you want SQL-adjacent query syntax, minimal runtime overhead (especially relevant for edge/serverless), and no separate code-generation step. Use Prisma when you prefer its more abstracted, declarative query style, want Prisma Studio's GUI for data browsing, or your team is already deeply familiar with its ecosystem.
Drizzle ORM in Production
Drizzle's small footprint makes it a natural fit alongside edge-friendly frameworks like Hono — the two are commonly paired specifically because neither adds meaningful cold-start overhead. Review generated migrations before applying to production the same way you would with any ORM's migration tooling; automated schema diffing is usually correct but not infallible for complex changes like column type conversions.
If you're choosing an ORM for a new edge-deployed or serverless-heavy project, Drizzle's lighter runtime is worth weighing seriously against Prisma's more mature tooling — the right choice depends on whether cold-start footprint or GUI tooling matters more for your specific deployment target.