Drizzle ORM
Pathrule2 Rules • 2 Memories • 1 Skill
Guardrails for teams using Drizzle ORM as their type-safe SQL layer. It keeps the TypeScript schema as the single source of truth, enforces a generate-then-migrate workflow with drizzle-kit, and steers queries toward inferred types, relations v2, and safe transactions. Use it to stop schema drift and silent N+1 patterns before they ship.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
Rules
2Schema is the single source of truth/src/db/schemahighstrictChange tables in the TypeScript schema, never in the live database.
| 1 | Treat the Drizzle TypeScript schema as the only place a table shape is defined. |
| 2 | |
| 3 | - Edit column and table definitions in `src/db/schema`, then run `drizzle-kit generate` to produce the SQL migration. |
| 4 | - Never alter columns directly in the database or hand-edit a generated migration's SQL after it is committed. |
| 5 | - Export inferred types with `typeof users.$inferSelect` and `typeof users.$inferInsert` instead of redeclaring row shapes by hand. |
| 6 | - Define foreign keys, indexes, and constraints in the schema so `drizzle-kit` can diff and version them. |
Generate then migrate, never push to production/drizzlehighadvisoryCommit generated SQL migrations and apply them with drizzle-kit migrate.
| 1 | Use the `generate` + `migrate` workflow for anything that touches a shared or production database. |
| 2 | |
| 3 | - Run `drizzle-kit generate` and commit the resulting file in `drizzle/` alongside the schema change in the same PR. |
| 4 | - Apply migrations with `drizzle-kit migrate` (or `migrate()` at deploy time), and reserve `drizzle-kit push` for throwaway local prototyping only. |
| 5 | - Hand-write the reverse SQL for destructive changes (dropping or retyping a column) and test it on staging first. |
| 6 | - Add a CI check that fails when a schema diff exists with no matching migration file, so drift cannot ship. |
Memories
2Drizzle versions and config baseline (2026)/src/dbCurrent package versions, dialect-based config, and the relations v2 shape.
| 1 | Pin to the current Drizzle line and use the dialect-based config introduced in the v1 betas. |
| 2 | |
| 3 | - Runtime is `drizzle-orm` and the CLI is `drizzle-kit`; the stable line is the 0.4x releases with v1.0.0 in beta as of mid-2026, so confirm the exact pinned version in `package.json` before relying on v1-only APIs. |
| 4 | - `drizzle.config.ts` uses `dialect` (`"postgresql"`, `"mysql"`, or `"sqlite"`) plus `schema` and `out` paths; the older `driver` key is gone in the v1 config. |
| 5 | - Relations v2 centralizes relations: define them once with `defineRelations` in a dedicated file and pass that object to `drizzle()` instead of attaching `relations()` per table. |
| 6 | - Keep one shared `db` client instance per process; pass it (or a `tx`) into functions rather than constructing new connections per call. |
Querying patterns: relations, transactions, prepared statements/src/dbUse the relational query API, atomic transactions, and prepared statements.
| 1 | Prefer Drizzle's higher-level query APIs over hand-rolled joins and per-row loops. |
| 2 | |
| 3 | - Load nested data with `db.query.users.findMany({ with: { posts: true } })` so related rows come back in one round trip instead of an N+1 loop. |
| 4 | - Wrap multi-statement writes in `db.transaction(async (tx) => { ... })` and use only `tx` inside; throwing rolls everything back, and nested calls become savepoints. |
| 5 | - For hot paths, build a prepared statement once with `.prepare()` and bind values via `sql.placeholder('name')`, then call `.execute({ name })`. |
| 6 | - Reach for the `sql` template tag for expressions Drizzle has no builder for, and keep user input as parameters rather than interpolated strings. |
Skills
1drizzle-orm-review/rootChecklist to review a Drizzle schema change, migration, and queries before merge.
| 1 | --- |
| 2 | name: drizzle-orm-review |
| 3 | description: Review a Drizzle ORM change before merge - schema source of truth, drizzle-kit migration hygiene, inferred types, relations, and safe transactions. |
| 4 | --- |
| 5 | |
| 6 | # Drizzle ORM review |
| 7 | |
| 8 | - [ ] Schema change lives in `src/db/schema` and the database was not edited directly. |
| 9 | - [ ] A `drizzle-kit generate` migration is committed in `drizzle/` for this schema diff. |
| 10 | - [ ] `drizzle-kit push` is not used against any shared or production database. |
| 11 | - [ ] Destructive changes (drop or retype a column) have tested reverse SQL and a staging plan. |
| 12 | - [ ] Row types come from `$inferSelect` / `$inferInsert`, not hand-written interfaces. |
| 13 | - [ ] `drizzle.config.ts` sets `dialect`, `schema`, and `out` (no legacy `driver` key). |
| 14 | - [ ] Relations use `defineRelations` (relations v2) in one place, not per-table `relations()`. |
| 15 | - [ ] Nested reads use the relational query API or a single join, not per-row loops. |
| 16 | - [ ] Multi-statement writes run inside `db.transaction` and use only `tx` internally. |
| 17 | - [ ] Hot-path queries use `.prepare()` with `sql.placeholder` and user input stays parameterized. |
Why this pattern
AI agents drift Drizzle schemas out of sync with the database and write untyped, N+1-prone queries.
Built for Backend and full-stack teams running Drizzle ORM on Postgres, MySQL, or SQLite..
Keeps your assistant from:
- Editing the database directly instead of regenerating migrations from the schema
- Hand-writing column types instead of inferring them with $inferSelect and $inferInsert
- Issuing per-row queries in loops instead of using relations or a single batched query
- License
- Apache-2.0
- Version
- 1.0.0
- Updated
- 2026-06-09