🟢The Backend
Lesson 74 of 117
30 min
Drizzle ORM
TS-first schema, SQL-like, edge-friendly, lighter bundle.
Drizzle is the ORM that feels like raw SQL but gives you full type safety on top. No DSL to memorize, no codegen step, no "black box" query engine. It is the default choice for new edge-deployed TypeScript projects in 2026 because it bundles tiny, runs anywhere, and reads almost like the SQL you would have written by hand.
Install
bash
pnpm add drizzle-orm pg
pnpm add -D drizzle-kit @types/pgDefine schema in TypeScript
src/db/schema.ts
import { pgTable, serial, text, timestamp, integer, boolean } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
authorId: integer("author_id").references(() => users.id),
title: text("title").notNull(),
body: text("body"),
published: boolean("published").default(false).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] }),
}));Connect the database
src/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 });Edge runtimes use the HTTP driver instead:
ts
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);Query: two flavors
Drizzle gives you a query builder that looks like SQL and a relational API that looks like Prisma. Pick whichever fits the moment.
Query builder (SQL-shaped)
ts
import { eq, and, desc } from "drizzle-orm";
import { db } from "@/db";
import { posts, users } from "@/db/schema";
// SELECT * FROM posts WHERE published = true ORDER BY id DESC LIMIT 10
const recent = await db
.select()
.from(posts)
.where(eq(posts.published, true))
.orderBy(desc(posts.id))
.limit(10);
// JOIN
const rows = await db
.select({
postId: posts.id,
title: posts.title,
authorName: users.name,
})
.from(posts)
.innerJoin(users, eq(users.id, posts.authorId))
.where(eq(posts.published, true));Relational query API
ts
const user = await db.query.users.findFirst({
where: (u, { eq }) => eq(u.email, "ada@example.com"),
with: { posts: { where: (p, { eq }) => eq(p.published, true) } },
});
// user is fully typed: { id, email, name, createdAt, posts: Post[] }Inserts, updates, deletes
ts
const [user] = await db.insert(users).values({
email: "ada@example.com",
name: "Ada",
}).returning();
await db.update(users)
.set({ name: "Ada Lovelace" })
.where(eq(users.id, user.id));
await db.delete(posts).where(eq(posts.authorId, user.id));Migrations with drizzle-kit
drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: { url: process.env.DATABASE_URL! },
});bash
# generate SQL migration from schema changes
pnpm drizzle-kit generate
# apply to dev DB
pnpm drizzle-kit migrate
# fast iterate without migrations (dev only)
pnpm drizzle-kit push
# inspect data
pnpm drizzle-kit studioGenerate vs push
generate creates a versioned SQL file you commit and run in CI. push diffs your schema directly to the DB without a migration file. Use generate for production, push for fast iteration.
Transactions
ts
await db.transaction(async (tx) => {
const [order] = await tx.insert(orders).values({ userId, total }).returning();
await tx.insert(orderItems).values(items.map(i => ({ ...i, orderId: order.id })));
});Why teams pick Drizzle
- Edge-friendly. Tiny bundle, no runtime engine, works in Cloudflare Workers and Vercel Edge.
- SQL fluency stays useful. Knowing SQL helps you write queries; with Prisma you had to learn its DSL.
- Zero codegen step for types. Schema = source of truth.
- Open-source migration tool. No proprietary cloud step required.
Where Drizzle hurts a bit
- Less batteries than Prisma (no Studio with magic forms, no Accelerate equivalent that ships in the box).
- The relational API is newer and sometimes lags behind the query builder in features.
- More boilerplate when defining relations.
Quiz
Quiz1 / 3
What is the main practical advantage of Drizzle over Prisma in 2026?
Recap
- Schema is a plain TS file. No DSL.
- Two query styles: SQL-shaped builder + relational findX API.
- drizzle-kit:
generate,migrate,push,studio. - Edge-ready, tiny bundle, no codegen.
- Pick Drizzle for edge, performance, and SQL fluency.