01 - What is Drizzle
📋 Jump to TakeawaysThe Short Version
Drizzle is a TypeScript ORM that stays as close to SQL as possible. If you know SQL, you already know most of Drizzle. It gives you type safety and autocompletion without hiding the SQL underneath.
Unlike heavier ORMs, Drizzle is a thin layer over SQL. You write queries that look like SQL, and they compile to exactly the SQL you'd expect.
// Drizzle query
const allUsers = await db.select().from(users);
// Runs: SELECT * FROM users
// With a filter
const activeUsers = await db.select().from(users).where(eq(users.active, true));
// Runs: SELECT * FROM users WHERE active = trueThe Three Parts
Drizzle has three separate packages:
| Package | What it does |
|---|---|
drizzle-orm |
The ORM itself. Schema definitions, queries, type inference |
drizzle-kit |
CLI tool. Migrations, schema push, introspection |
drizzle-studio |
Visual database browser (comes with drizzle-kit) |
You always need drizzle-orm. You almost always need drizzle-kit for managing your database schema.
Drizzle vs Prisma
If you've used Prisma, here's how Drizzle differs:
| Prisma | Drizzle | |
|---|---|---|
| Schema | .prisma file (custom DSL) |
TypeScript files |
| Queries | Custom query engine | SQL-like syntax |
| Code generation | Required (prisma generate) |
None needed |
| Bundle size | Heavy (query engine binary) | Lightweight (~50KB) |
| Edge support | Limited | Full (Vercel, Cloudflare) |
// ❌ Prisma - custom syntax, needs codegen
const users = await prisma.user.findMany({
where: { active: true },
select: { name: true, email: true }
});
// ✅ Drizzle - reads like SQL, no codegen
const users = await db
.select({ name: usersTable.name, email: usersTable.email })
.from(usersTable)
.where(eq(usersTable.active, true));Neither is "better." Prisma is more abstracted. Drizzle is closer to SQL. Pick what fits your mental model.
Setup
Start a new project and install the packages:
mkdir drizzle-app && cd drizzle-app
npm init -y
npm install drizzle-orm postgres
npm install -D drizzle-kit tsx typescriptAdd "type": "module" to your package.json so top-level await works:
{
"type": "module"
}Setting Up the Database
You need a PostgreSQL database. For local development, install PostgreSQL or use Docker:
# Docker (quickest)
docker run --name mydb -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres
# Create a database
docker exec -it mydb psql -U postgres -c "CREATE DATABASE myapp"Create a .env file with your connection string:
DATABASE_URL="postgresql://postgres:password@localhost:5432/myapp"Defining Your Schema
Your tables are defined in TypeScript. Create src/db/schema.ts:
// src/db/schema.ts
import { pgTable, serial, text } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
});This is just a preview. The next lesson covers schema definitions in detail.
Configuration
Create drizzle.config.ts in your project root. This tells drizzle-kit where your schema is and how to connect:
// 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!,
},
});Now push your schema to create the tables in the database:
npx drizzle-kit pushIf you get unknown command 'push', update drizzle-kit: npm install -D drizzle-kit@latest.
Connecting to the Database
Create your database connection in src/db/index.ts:
// src/db/index.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client);
export { client };db is your query interface. client is the raw connection (you'll need it to close the connection when your script finishes).
Running Your First Query
Create src/app.ts:
// src/app.ts
import { db, client } from "./db";
import { users } from "./db/schema";
import { eq } from "drizzle-orm";
// Clean up from previous runs
await db.delete(users).where(eq(users.email, "alice@example.com"));
// Insert a user
await db.insert(users).values({ name: "Alice", email: "alice@example.com" });
// Query all users
const allUsers = await db.select().from(users);
console.log(allUsers);
// [{ id: 1, name: "Alice", email: "alice@example.com" }]
// Close the connection when done
await client.end();Run it:
npx tsx --env-file=.env src/app.tstsx runs TypeScript directly. The --env-file flag loads your .env so process.env.DATABASE_URL is available.
Project Structure
Your project should now look like this:
drizzle-app/
src/
db/
index.ts # database connection
schema.ts # table definitions
app.ts # your application code
drizzle.config.ts # drizzle-kit config
.env # DATABASE_URL goes here
package.jsonKeep your schema in one file to start. You can split it into multiple files later as your schema grows.
Key Takeaways
- Drizzle is a TypeScript ORM that writes queries close to raw SQL
- Three parts:
drizzle-orm(queries),drizzle-kit(CLI/migrations), Drizzle Studio (visual browser) - No code generation step, unlike Prisma
- Lightweight and works at the edge (Vercel, Cloudflare Workers)
- Setup: install packages, create
.env, define schema, rundrizzle-kit push, connect and query - The
dbobject is your main interface for all database operations