Drizzle ORM Skill
Use this skill whenever a task touches Drizzle ORM, Drizzle Kit, or Drizzle Studio—schema modeling, migrations, Drizzle Gel edge DB, or writing SQL-safe queries from Bun services.
Quick Start
- •Install tooling (Bun):
sh
bun add drizzle-orm drizzle-kit bun add -D drizzle-kit # plus driver: pg | @neondatabase/serverless | postgres | better-sqlite3 | mysql2 | @libsql/client
- •Init config:
bunx drizzle-kit initcreatesdrizzle.config.ts. Keep paths relative to repo root. - •Author schema: create
src/db/schema/*.tsexporting tables viapgTable,mysqlTable,sqliteTable, orgelTable. - •Pick a migration flow: use the chooser in
references/migration-flows.md. - •Run queries: instantiate the right driver (
drizzle(client)) and write strongly typed queries with chains like.select().where(eq(users.id, userId)).
Consult references/drizzle-kit-cli.md for CLI invocations and references/migration-flows.md for flow-by-flow detail.
Project Setup & Config
- •Always set
"packageManager": "bun@<version>"and expose scripts such as:jsonc{ "scripts": { "drizzle:generate": "bunx drizzle-kit generate", "drizzle:push": "bunx drizzle-kit push", "drizzle:studio": "bunx drizzle-kit studio" } } - •Example
drizzle.config.ts:tsimport { defineConfig } from "drizzle-kit"; export default defineConfig({ dialect: "postgresql", // postgresql | mysql | sqlite | op-sqlite | gel schema: "./src/db/schema/index.ts", out: "./drizzle", strict: true, migrations: { schema: "public", table: "__drizzle_migrations", }, }); - •Keep schema export(s) centralized (e.g.,
src/db/schema/index.tsre-exporting table objects). When working in monorepos, reference the compiled path used by each package.
Schema Design Patterns
- •Favor table-per-file modules with
export const users = pgTable("users", { ... })plus indexes (index,uniqueIndex) and relations viarelations(users, ({ many }) => ({ posts: many(posts) })). - •Derive types:
ts
export type User = typeof users.$inferSelect; export type NewUser = typeof users.$inferInsert;
- •Use helpers such as
varchar,timestamp,boolean, andjsonb(Postgres) ortext,integer,blob(SQLite). Drizzle enforces column constraints at type level; lean on.defaultNow(),.references(() => other.id, { onDelete: "cascade" }), etc. - •For enums, prefer
pgEnum("role", ["admin", "member"])ormysqlEnum.
Dialects & Connections
| Target | Driver snippet |
|---|---|
| PostgreSQL (server) | import { drizzle } from "drizzle-orm/node-postgres"; const client = new Client({ connectionString }); await client.connect(); export const db = drizzle(client); |
| Bun SQL (Postgres via Bun.sql) | import { drizzle } from "drizzle-orm/bun-sql"; import { SQL } from "bun"; const sql = new SQL(process.env.DATABASE_URL!); export const db = drizzle({ client: sql }); |
| Postgres serverless/HTTP | import { drizzle } from "drizzle-orm/neon-http"; import { neon } from "@neondatabase/serverless"; const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql); |
| MySQL | import { drizzle } from "drizzle-orm/mysql2"; const pool = mysql.createPool(process.env.DATABASE_URL!); export const db = drizzle(pool); |
| SQLite (file) | import { Database } from "bun:sqlite"; import { drizzle } from "drizzle-orm/bun-sqlite"; export const db = drizzle(new Database("app.db")); |
| Turso/libSQL | import { createClient } from "@libsql/client"; import { drizzle } from "drizzle-orm/libsql"; |
| Drizzle Gel (edge) | import { drizzle } from "drizzle-orm/gel"; import { connect } from "@drizzle-team/gel"; |
Tips:
- •Always close pools on process exit to avoid hanging tests.
- •When mixing HTTP handlers, instantiate the client once per process (Bun global) to reuse connections.
- •Use
drizzle-orm/bun-sqlwhenever you rely on Bun's nativeSQLdriver; import the adapter and either pass aDATABASE_URLstring or supply an explicitSQLclient when you need manual control. citeturn0search2 - •For Bun SQL, you can skip the explicit
SQLclient and calldrizzle(process.env.DATABASE_URL!)directly whenDATABASE_URLuses a supported Postgres/MySQL/SQLite URI; fall back tonew SQL()when you need manual adapter selection. citeturn0search2 - •Bun 1.2.0 has a known issue with concurrent SQL statements; keep multi-query workloads sequential or follow the upstream GitHub issue before turning on parallel query execution. citeturn0search9
Migrations & Change Management
- •Choose a flow (options 1‑6) via
references/migration-flows.md. - •Run CLI using Bun (e.g.,
bun run drizzle:generate,bunx drizzle-kit push). - •Commit generated SQL plus
drizzle/metasnapshots so diffs remain deterministic. - •For CI migrations, add a job step:
sh
bun install bun run drizzle:generate # optional bun run drizzle:migrate # custom script running node script or `bunx drizzle-kit migrate`
- •The migrations table defaults to
__drizzle_migrations; override indrizzle.config.tswhen schemas differ per environment.
Query & Builder Patterns
- •Basic select:
ts
const result = await db.select().from(users).where(eq(users.id, userId));
- •Joins:
ts
const rows = await db .select({ userId: users.id, note: notes.body }) .from(users) .leftJoin(notes, eq(notes.userId, users.id)) .where(and(eq(users.status, "active"), gt(notes.createdAt, cutoff))); - •Inserts/updates:
ts
await db.insert(users).values(newUser).returning({ id: users.id }); await db.update(users).set({ status: "archived" }).where(eq(users.id, userId)); - •Raw SQL fallback:
await db.execute(sql\select now()`);` when features lag behind builder support.
Tooling & Observability
- •Drizzle Studio:
bun run drizzle:studiolaunches a local viewer with schema browsing, filtering, and editing. Keep it dev-only. - •Introspection:
bunx drizzle-kit introspect --logprints database metadata before generating schema files. - •Snapshots: snapshot JSON files under
drizzle/metarecord table definitions; never hand-edit them. - •Logging: wrap driver clients with instrumentation (e.g.,
neonprovidesneonConfig.fetchEndpoint) or uselogger: truein Drizzle connections when debugging SQL.
Testing & Verification
- •For unit tests, point SQLite/Bun projects to an in-memory DB:
new Database(":memory:"). - •Use transaction wrappers (
db.transaction(async (tx) => { ... })) around multi-step mutations to ensure atomicity. - •Pair schema tests with
bun testto verify relations/resolvers compile (e.g., import schema inside tests to catch type regressions).
References
Load these reference files when you need detailed command syntax or when selecting a migration strategy.