Vai al contenuto
Pathrule

pattern

Questa pagina è disponibile solo in inglese.

Drizzle ORM

Pathrule2 regole • 3 memorie • 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.

Mappa dei percorsi suggerita

Pathrule colloca ogni elemento sul percorso corrispondente, così il tuo assistente lo vede solo dove serve. È l'ambito che ottieni con l'importazione; puoi modificarlo nel tuo workspace.

/ radice del workspace
drizzle-orm-review
src/
db/
Drizzle versions and config baseline (2026)
Relations v2: defineRelations and the relational query API
Querying patterns: transactions and prepared statements
schema/
Schema is the single source of truth
drizzle/
Generate then migrate; never push to a shared database

Regole

2
Schema is the single source of truth/src/db/schemaaltavincolanteChange tables in the TypeScript schema, never in the live database; export inferred types, never hand-write row shapes.
1Treat 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; both create a drift that silently breaks generated types.
5- Export inferred types with `typeof users.$inferSelect` and `typeof users.$inferInsert` instead of redeclaring row shapes by hand. Hand-written interfaces fall out of sync whenever a column is added or removed.
6- Define foreign keys, indexes, and constraints in the schema so `drizzle-kit` can diff and version them alongside column changes.
Generate then migrate; never push to a shared database/drizzlealtaconsultivaCommit generated SQL migrations and apply them with drizzle-kit migrate; reserve push for throwaway local prototyping only.
1Use 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 the `migrate()` helper at deploy time). Reserve `drizzle-kit push` for local throwaway prototyping only; it overwrites the database schema without recording a migration file.
5- Hand-write the reverse SQL for destructive changes (dropping or retyping a column) and test it on a copy of the staging schema first.
6- Add a CI check that runs `drizzle-kit check` (or equivalent diff) and fails when the schema has a diff with no corresponding migration file, so drift cannot reach main.

Memorie

3
Drizzle versions and config baseline (2026)/src/dbCurrent package versions, dialect-based config, and shared db-client conventions.
1Pin to the current Drizzle line and use the dialect-based config introduced in the 0.40+ releases.
2 
3- Runtime is `drizzle-orm` and the CLI is `drizzle-kit`. The stable line as of mid-2026 is 0.4x; a v1.0.0 release is in progress, so confirm the exact pinned version in `package.json` before relying on APIs documented only for v1.
4- `drizzle.config.ts` uses `dialect` (`"postgresql"`, `"mysql"`, or `"sqlite"`) plus `schema` and `out` paths; the older `driver` key is removed in the new config format.
5- Keep one shared `db` client instance per process. Pass the `db` (or a `tx`) into functions as a parameter rather than importing a singleton inside deeply nested modules, so transaction boundaries stay explicit.
6- For Postgres, create the client with `drizzle(pool, { schema })` where `pool` is a `node-postgres` Pool or `postgres` (postgres.js) instance. Passing the schema object enables the relational query API.
7 
8See /src/db for querying patterns and /src/db for relations v2 setup.
Relations v2: defineRelations and the relational query API/src/dbHow to declare relations once with defineRelations, pass them to drizzle(), and use the with: {} query API without N+1 loops.
1Relations v2, introduced in Drizzle 0.36+, moves relation declarations out of individual table files and into a centralized definition. Agents frequently write the old per-table `relations()` pattern or skip the relational API entirely and write N+1 loops.
2 
3- Define all relations in a dedicated file (for example `src/db/relations.ts`) using `defineRelations` exported from `drizzle-orm`:
4 ```ts
5 import { defineRelations } from 'drizzle-orm';
6 import * as schema from './schema';
7 
8 export const relations = defineRelations(schema, (r) => ({
9 users: {
10 posts: r.many.posts({ from: schema.users.id, to: schema.posts.authorId }),
11 },
12 posts: {
13 author: r.one.users({ from: schema.posts.authorId, to: schema.users.id }),
14 },
15 }));
16 ```
17- Pass the relations object to `drizzle()` alongside the schema: `const db = drizzle(pool, { schema, relations })`.
18- Query nested data with `db.query.users.findMany({ with: { posts: true } })`. This issues a single SQL query (a lateral join), not a loop of per-row queries. The old per-table `relations()` helper is still supported for compatibility but new code should use `defineRelations`.
19- For optional or filtered eager loads, pass `{ with: { posts: { where: eq(posts.published, true) } } }` rather than filtering in application code after loading all rows.
Querying patterns: transactions and prepared statements/src/dbAtomic transactions with savepoints and prepared statements for hot paths.
1Prefer Drizzle's built-in abstractions for transactions and repeated queries over hand-rolled SQL.
2 
3- Wrap multi-statement writes in `db.transaction(async (tx) => { ... })` and use only `tx` inside the callback. Throwing (or returning a rejected promise) rolls everything back automatically. Nested `db.transaction()` calls on the same `tx` become savepoints.
4- For hot paths, build a prepared statement once with `.prepare('name')` and bind values via `sql.placeholder('name')`, then call `.execute({ name: value })`. Prepared statements send only bind parameters on repeated calls and let Postgres skip re-planning.
5- Reach for the `sql` template tag for expressions Drizzle has no query builder for. Always pass user input as parameters in the template (`sql`SELECT * FROM users WHERE id = ${userId}``), never as interpolated string concatenation.
6- Avoid `db.execute(sql.raw(...))` with unsanitized strings; it bypasses Drizzle's parameter binding and introduces SQL injection.

Skill

1
drizzle-orm-review/rootChecklist to review a Drizzle schema change, migration, and queries before merge.
1---
2name: drizzle-orm-review
3description: Review a Drizzle ORM change before merge. Use on schema modifications, drizzle-kit migrations, relation definitions, query patterns, and transaction logic.
4---
5 
6# Drizzle ORM review
7 
8## Schema and migrations
9- [ ] Schema change lives in `src/db/schema` and the database was not edited directly.
10- [ ] A `drizzle-kit generate` migration is committed in `drizzle/` for every schema diff.
11- [ ] `drizzle-kit push` is not used against any shared or production database.
12- [ ] Destructive changes (drop or retype a column) have tested reverse SQL and a staging plan.
13- [ ] Row types come from `$inferSelect` / `$inferInsert`, not hand-written interfaces.
14 
15## Config and client
16- [ ] `drizzle.config.ts` sets `dialect`, `schema`, and `out`; no legacy `driver` key.
17- [ ] One shared `db` client instance per process; the `db` or `tx` is passed into functions, not re-imported from a singleton.
18 
19## Relations and queries
20- [ ] Relations use `defineRelations` in a centralized file and are passed to `drizzle(pool, { schema, relations })`; no per-table `relations()` on new code.
21- [ ] Nested reads use `db.query.<table>.findMany({ with: { ... } })`, not per-row loops.
22- [ ] Multi-statement writes run inside `db.transaction` using only `tx` internally.
23- [ ] Hot-path queries use `.prepare()` with `sql.placeholder`; user input stays parameterized and `sql.raw` is not used with unsanitized input.

Perché questo pattern

AI agents drift Drizzle schemas out of sync with the database and write untyped, N+1-prone queries.

Pensato per: Backend and full-stack teams running Drizzle ORM on Postgres, MySQL, or SQLite..

Aiuta il tuo assistente a evitare:

  • 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
Licenza
Apache-2.0
Versione
1.0.1
Aggiornato
2026-08-25
Vedi il sorgente