Drizzle ORM is a modern, type-safe SQL ORM for
TypeScript.
It's built to be lightweight, explicit, and compatible with multiple
databases,
while giving you full control over SQL.
Drizzle Client
Drizzle doesn't generate a client like Prisma - instead, you define your schema
in TypeScript and pass it to a drizzle()
instance that manages queries.
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
import { env } from "@/env"
import * as schema from "./schema"
const globalForDb = globalThis as unknown as {
conn: postgres.Sql | undefined
}
const conn = globalForDb.conn ?? postgres(env.DATABASE_URL)
if (env.NODE_ENV !== "production") globalForDb.conn = conn
export const db = drizzle(conn, { schema })
Here:
postgres
is the database driverschema
is imported from your Drizzle schema definitiondb
is your Drizzle instance - similar to Prisma'sPrismaClient
Drizzle works with multiple drivers (postgres
, mysql
, sqlite
, etc.)
Defining a Schema
Unlike Prisma's .prisma
file, Drizzle schemas are written directly in
TypeScript. Below is the default schema for a Lx2 application when initialized
with Drizzle:
import { sql } from "drizzle-orm"
import { index, pgTableCreator } from "drizzle-orm/pg-core"
export const createTable = pgTableCreator((name) => `drizzle-test_${name}`)
export const post = createTable(
"post",
(d) => ({
id: d.integer().primaryKey().generatedByDefaultAsIdentity(),
name: d.varchar({ length: 255 }).notNull(),
createdAt: d
.timestamp({ withTimezone: true })
.default(sql`CURRENT_TIMESTAMP`)
.notNull(),
updatedAt: d.timestamp({ withTimezone: true }).$onUpdate(() => new Date()),
}),
(t) => [index("post_name_idx").on(t.name)],
)
Key Concepts:
pgTableCreator
is a custom table creator function used to prefix table names- Column types (
integer
,varchar
,timestamp
) map to SQL types - Constraints (
primaryKey
,notNull
) enforce rules - No separate migration file is required for schema definition - migrations are generated from these TypeScript files
Basic CRUD Operations
- Create:
import { post as postTable } from "@/server/db/schema"
const newPost = await db
.insert(postTable)
.values({
id: 1,
name: "New Post",
})
.returning()
console.log(newPost)
- Read:
import { post as postTable } from "@/server/db/schema"
const posts = await db.query.post.findMany()
console.log(posts)
const post = await db.query.post.findFirst({
where: eq(postTable.id, 1),
})
console.log(post)
- Update:
import { post as postTable } from "@/server/db/schema"
const updatedPost = await db
.update(postTable)
.set({
name: "Updated Post",
})
.where(eq(postTable.id, 1))
.returning()
console.log(updatedPost)
- Delete:
import { post as postTable } from "@/server/db/schema"
const deletedPost = await db
.delete(postTable)
.where(eq(postTable.id, 1))
.returning()
console.log(deletedPost)
Migrations
Drizzle migrations are code-based and generated using the CLI.
Remember to swap out the below package manager for your preferred one (e.g.,
npm
, bun
, yarn
).
Common Commands:
pnpm db:generate
-> Generate SQL migrations from your TypeScript schemapnpm db:migrate
-> Apply pending migrations to the databasepnpm db:push
-> Apply migrations directly to the databasepnpm db:studio
-> Visual database browser
Workflow:
-
Modify your TypeScript schema (
schema.ts
) -
Run:
pnpm db:generate
-
Apply the migrations:
pnpm db:migrate
-
Your changes are not in sync with the database
Drizzle lets you write raw SQL for special cases - but it's type-checked.
See Raw SQL Queries for more information.
Drizzle Studio
Drizzle Studio is a browser UI for your database. To access it, run:
pnpm db:studio
The command will return a URL to access Drizzle Studio in your browser, where you can:
- Browse tables
- View data
- Run queries
- Edit records
Useful Resources
Resource | Description |
---|---|
Drizzle Documentation | Comprehensive guide to Drizzle ORM |
Drizzle GitHub Repository | Source code and issues |
Drizzle Kit | CLI for migrations & DB tools |
Drizzle Discord Community | Join the community for support and discussions |