Bu sayfa yalnızca İngilizce olarak mevcut.
Drizzle ORM
Pathrule2 kural • 3 hafıza kaydı • 1 beceri
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.
Önerilen yol haritası
Pathrule her parçayı eşleşen yola yerleştirir; böylece asistanınız onu yalnızca ait olduğu yerde görür. İçe aktardığınızda kapsam böyle kurulur; isterseniz çalışma alanınızda değiştirebilirsiniz.
Kurallar
2Schema is the single source of truth/src/db/schemayüksekkatıChange tables in the TypeScript schema, never in the live database; export inferred types, never hand-write row shapes.
| 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; 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. |
Hafıza kayıtları
3Drizzle versions and config baseline (2026)/src/dbCurrent package versions, dialect-based config, and shared db-client conventions.
| 1 | Pin 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 | |
| 8 | See /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.
| 1 | Relations 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.
| 1 | Prefer 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. |
Beceriler
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. 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. |
Neden bu kalıp
AI agents drift Drizzle schemas out of sync with the database and write untyped, N+1-prone queries.
Hedef kitle: Backend and full-stack teams running Drizzle ORM on Postgres, MySQL, or SQLite..
Asistanınızı şunlardan alıkoyar:
- 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
- Lisans
- Apache-2.0
- Sürüm
- 1.0.1
- Güncellendi
- 2026-08-25