TL;DR
Drizzle ORM is a TypeScript-first ORM that generates SQL you can actually read. Zero runtime overhead, full type safety, and it works with PostgreSQL, MySQL, SQLite, and serverless databases.
What Is Drizzle?
Drizzle ORM is SQL-like by design:
- SQL-like syntax — if you know SQL, you know Drizzle
- Zero overhead — thin SQL wrapper, no query builder bloat
- Full type safety — schema-to-TypeScript type inference
- Serverless ready — works with Neon, PlanetScale, Turso, D1
- Migrations — automatic migration generation
- Drizzle Studio — visual database browser
- Free — Apache 2.0
Quick Start
npm install drizzle-orm postgres
npm install -D drizzle-kit
Define Schema
// src/db/schema.ts
import { pgTable, serial, text, timestamp, integer, boolean } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
age: integer("age"),
active: boolean("active").default(true),
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),
published: boolean("published").default(false),
createdAt: timestamp("created_at").defaultNow(),
});
Queries (SQL-like)
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { users, posts } from "./schema";
import { eq, gt, like, and, desc, count } from "drizzle-orm";
const sql = postgres(process.env.DATABASE_URL!);
const db = drizzle(sql);
// SELECT — feels like SQL!
const allUsers = await db.select().from(users);
// WHERE
const activeUsers = await db
.select()
.from(users)
.where(and(eq(users.active, true), gt(users.age, 18)));
// JOIN
const usersWithPosts = await db
.select({
userName: users.name,
postTitle: posts.title,
})
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt))
.limit(10);
// INSERT
const newUser = await db
.insert(users)
.values({ name: "Alice", email: "alice@example.com", age: 30 })
.returning();
// UPDATE
await db
.update(users)
.set({ active: false })
.where(eq(users.id, 1));
// DELETE
await db.delete(users).where(eq(users.id, 1));
// Aggregations
const stats = await db
.select({
total: count(),
avgAge: avg(users.age),
})
.from(users)
.where(eq(users.active, true));
Migrations
// 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!,
},
});
# Generate migration
npx drizzle-kit generate
# Apply migration
npx drizzle-kit migrate
# Open Drizzle Studio (visual DB browser)
npx drizzle-kit studio
Serverless (Neon, Turso, D1)
// Neon Serverless
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
const sql = neon(process.env.DATABASE_URL!);
const db = drizzle(sql);
// Turso (LibSQL)
import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";
const client = createClient({ url: "libsql://...", authToken: "..." });
const db = drizzle(client);
// Cloudflare D1
import { drizzle } from "drizzle-orm/d1";
const db = drizzle(env.DB);
Drizzle vs Prisma vs TypeORM
| Feature | Drizzle | Prisma | TypeORM |
|---|---|---|---|
| Syntax | SQL-like | Custom DSL | OOP |
| Bundle size | 35 KB | 2+ MB | 500 KB+ |
| Serverless | Perfect | Adapter needed | Poor |
| Raw SQL | Easy | $queryRaw | QueryBuilder |
| Type safety | Full inference | Generated | Decorators |
| Migrations | Auto-generate | Auto-generate | Auto/manual |
| Edge runtime | Yes | Limited | No |
Resources
- Drizzle Documentation
- GitHub Repository — 25K+ stars
- Drizzle Studio
- Examples
Storing scraped web data? My Apify tools extract structured data from any website — load it into your database with Drizzle ORM for type-safe data pipelines. Questions? Email spinov001@gmail.com
Top comments (0)