Drizzle ORM 0.45
Drizzle ORM 0.45.4 + Drizzle Kit 0.31.11 code, configuration, migrations and tooling without 1.0 beta APIs or pre-0.45 habits; any dialect, driver, runtime or app shape. Rule IDs DZ-V/T/C/S/Q/R/X/M/A/Z; NEED TO blocks.
Set this profile
ccpp cpo profile set drizzle-ormInside Claude Code, prefix the command with ! to run it in the session. Downloading a profile needs a Builder or Team plan; listing them does not.
Policies
114 policies, grouped by what they guard.
Secrets & private data 3
Credentials, keys and personal data stay out of files, logs, commands and the conversation.
dz-c02-command-line-credentialsDZ-C02 · drizzle-kit commands do not carry database passwords (--password, user:password@ URLs)
dz-c02-literal-credentialsDZ-C02 · Database passwords (connection strings, connection option objects) are not written into source or config; placeholders and application passwords pass
dz-q11-sensitive-whole-rowsDZ-Q11 · Whole-row reads of tables holding secrets (password, token, secret…) select explicit fields
Untrusted input 4
Requests, downloads and tool output are validated before they reach SQL, a shell, eval or a deserializer.
dz-q01-unparameterized-sqlDZ-Q01 · Values reach SQL as parameters: no dynamic text in sql.raw() or untagged strings passed to execute()
dz-q02-untrusted-identifiersDZ-Q02 · Table, column and sort choices from untrusted input go through a closed mapping, not table[input] or sql.identifier(input)
dz-q05-untrusted-write-objectsDZ-Q05 · Untrusted request objects are not passed or spread whole into .values()/.set(): writable fields are allowlisted
dz-q14-unbounded-input-limitsDZ-Q14 · limit/offset taken from untrusted input are bounded and validated
Destructive actions 7
Deletions, overwrites and irreversible commands wait for the user or a scoped target.
dz-m04-destructive-generated-sqlDZ-M04 · Generated migration SQL that drops, renames, narrows or adds constraints is reviewed before it is applied
dz-m04-push-forceDZ-M04 · drizzle-kit push --force (auto-approves data-loss statements) needs the user's confirmation
dz-m05-history-removalDZ-M05 · Deleting or rewriting recorded migrations (drizzle-kit drop, rm/mv/git rm in the migrations folder) needs the user's confirmation
dz-q03-unscoped-mutationDZ-Q03 · An executed Drizzle update or delete has a where predicate, unless the bulk write is a recorded exception
dz-q04-lsp-optional-predicateDZ-Q04 · A mutation's where argument is not typed SQL | undefined (language server: variables and helper results)
dz-q04-optional-predicateDZ-Q04 · A mutation's where predicate cannot collapse to undefined (all-optional and()/or(), undefined) or a tautology
Weakened checks 2
Suppressions, disabled lint or CI steps and relaxed strictness need a reason or the user's approval.
dz-a08-suppressed-diagnosticsDZ-A08 · Drizzle type errors are fixed, not suppressed with @ts-ignore, @ts-expect-error or @ts-nocheck
dz-z03-exception-markersDZ-Z03 · Exception markers (// dz-<rule>:) name one rule and give a reason; no blanket disabling of the profile
Type safety 1
No escapes from the type checker (any, unchecked casts, non-null assertions) and explicit contracts.
dz-a04-type-erasureDZ-A04/A08 · Drizzle tables, columns and results are not erased to any (casts, any-typed generic CRUD wrappers)
Error handling 1
Failures are propagated or handled, never swallowed or turned into fake success.
dz-a07-fabricated-successDZ-A07 · Query failures are not turned into empty results or defaults ([], null, {}, 0, false) that callers cannot tell apart
Performance & resource bounds 4
Timeouts, bounded retries and fan-out, reused clients, closed resources, no N+1 queries.
dz-c07-client-per-callDZ-C07 · Database clients and pools are created once per lifecycle, not inside query functions
dz-q16-queries-in-loopsDZ-Q16 · Queries that depend on each row of a loop are reviewed for N+1 (join, relational query, inArray batch)
dz-q17-filtering-in-jsDZ-Q17 · Unfiltered reads are not followed only by JavaScript filtering, counting or summing (push it into SQL)
dz-q20-index-evidenceDZ-Q20 · Several new indexes at once are justified by observed query plans (hint)
Architecture & boundaries 4
Declared layers and dependency directions, no import cycles, small modules, explicit dependencies.
dz-a01-schema-runtime-importsDZ-A01 · Schema modules stay declarative: no client, driver, framework, env or app-entry imports
dz-a02-data-access-boundaryDZ-A02 · Drizzle queries run only in the modules the project declares for data access (parameter dataAccessPaths)
dz-a03-repeated-scope-filtersDZ-A03 · A predicate repeated three times or more in a file (tenant, soft delete, ownership) becomes one small typed helper; primary-key lookups and join conditions are not scope filters
dz-a05-unused-helpersDZ-A05 · No speculative scaffolding: new private query helpers have a caller in the file
Databases & queries 32
Schemas, queries, transactions and the ORM used as intended.
dz-q06-where-accumulationDZ-Q06 · Predicates are combined explicitly: a second .where() on the same builder replaces the first
dz-q07-module-level-builderDZ-Q07 · Mutable query builders are created per call, not kept at module level and modified by functions
dz-q08-duplicated-read-chainsDZ-Q08 · Optional read filters are composed into one query instead of duplicated if/else chains (hint)
dz-q09-null-comparisonDZ-Q09 · Null tests use isNull/isNotNull, not eq/ne(column, null) or = NULL in SQL
dz-q10-sql-type-claimsDZ-Q10 · sql<number> over count/sum/avg on PostgreSQL decodes the value (mapWith, cast, count()) instead of claiming a type
dz-q12-unaliased-subquery-fieldDZ-Q12 · SQL fields selected in a subquery or CTE carry an alias (.as) before they are referenced outside it
dz-q13-unordered-paginationDZ-Q13 · Paginated reads (offset) have an orderBy, so pages are deterministic
dz-q15-unchecked-first-rowDZ-Q15 · The first row of a select or a findFirst result is checked for absence before it is dereferenced
dz-q18-check-then-insertDZ-Q18 · Insert-or-update is atomic: no existence check followed by insert/update without conflict handling or locking
dz-q19-unexecuted-builderDZ-Q19 · Drizzle mutations reach execution: a builder statement without await/return never runs; .execute() is awaited
dz-r01-relation-mappingDZ-R01 · one() relation mappings pair fields of the declaring table with as many references of the target table
dz-r02-ambiguous-relationsDZ-R02 · Several relations between the same two tables are disambiguated with relationName
dz-r03-nested-offsetDZ-R03 · Relational queries keep offset at the root: 0.45 types nested with-configs without offset
dz-r04-extras-aggregatesDZ-R04 · Relational extras hold aliased per-row SQL, not aggregates (count/sum/… need a subquery or a core query)
dz-r05-nested-filtersDZ-R05 · A nested with-where filters children, not parents (hint once per session)
dz-s01-duplicate-identitiesDZ-S01 · One declaration per table and per column name: no second table with the same SQL name, no repeated column name
dz-s03-keyless-tablesDZ-S03 · New tables have a primary key (keyless only for intentional append-only, import or log tables)
dz-s04-soft-relationsDZ-S04 · relations() is not a foreign key: a one() over a column without references()/foreignKey() is flagged
dz-s05-join-table-keysDZ-S05 · A join table of two foreign keys has a composite primary key or unique constraint over the pair
dz-s06-set-null-not-nullDZ-S06 · ON DELETE/UPDATE SET NULL only targets nullable columns
dz-s07-application-defaultsDZ-S07 · $defaultFn/$onUpdate are application-side only: no DDL, ignored by other writers (hint once per session)
dz-s08-null-overrides-defaultDZ-S08 · Inserts and updates do not turn an absent value into null (?? null), which overrides defaults and NOT NULL
dz-s09-mirrored-row-typesDZ-S09 · Row types are inferred from the table ($inferSelect/$inferInsert), not mirrored by hand next to it
dz-s10-type-hintsDZ-S10 · .$type<T>() and text enums are compile-time hints, not validation (hint once per session)
dz-s11-numeric-precisionDZ-S11 · bigint/numeric columns are not decoded as JavaScript number without a stated range
dz-s13-repeated-conversionsDZ-S13 · Repeated JSON/Date conversions of query values are centralized (customType, mapWith or one helper)
dz-s14-extension-provisioningDZ-S14 · Extension-dependent columns and indexes (pgvector, PostGIS, pg_trgm) come with a CREATE EXTENSION migration
dz-x01-independent-writesDZ-X01 · Several awaited writes in one function run in a transaction when they form one change
dz-x02-handle-outside-transactionDZ-X02 · Queries inside a transaction callback use its tx handle, not the outer database handle
dz-x03-swallowed-transaction-errorDZ-X03 · A failed write inside a transaction is rethrown or rolled back, never caught into a normal return
dz-x04-effects-in-transactionDZ-X04 · Irreversible external effects (HTTP, mail, queues, payments) are kept out of transaction callbacks
dz-x05-read-modify-writeDZ-X05 · Counters and balances are updated in SQL, not read into JavaScript, changed and written back
Schema migrations 9
Migrations come from the tool, applied history stays immutable and risky changes are rolled out safely.
dz-m01-schema-workflowDZ-M01 · Schema changes follow the declared workflow (migrationWorkflow): no push for migrations, no generate for push, nothing for external
dz-m02-push-over-migrationsDZ-M02 · push on a project that records migrations (a journal exists, workflow not declared) needs the user's confirmation
dz-m03-schema-without-migrationDZ-M03 · A turn that changes table/column/index declarations in a migrations project also generates the migration (once per stop)
dz-m05-applied-historyDZ-M05 · Committed migration SQL files are not edited or rewritten (add a forward migration instead)
dz-m06-unregistered-migrationDZ-M06 · A new SQL file in the migrations folder comes from drizzle-kit generate --custom (the journal registers it)
dz-m08-check-warningsDZ-M08 · drizzle-kit check warnings (entries the migrator would skip, collisions, malformed snapshots) are unresolved findings
dz-m09-journal-orderDZ-M09 · Journal entries keep strictly increasing when timestamps in entry order (merges of parallel branches included)
dz-m11-migrations-at-startupDZ-M11 · migrate() has one execution owner: not in request handlers or every server/worker start without a lock
dz-t09-custom-migratorDZ-T09 · Applying migrations uses drizzle-kit migrate or the driver's migrate(), not a hand-written journal reader or ledger
Versions & deprecated APIs 13
Code matches the installed version, without removed, renamed or deprecated APIs.
dz-v01-installed-versionDZ-V01/Z04 · At session start, Claude learns the installed drizzle-orm/drizzle-kit versions and whether this profile's 0.45/0.31 rules apply
dz-v02-v1-relations-apiDZ-V02 · Drizzle 1.0 relational APIs (defineRelations, db._query, r.one/r.many, object where/orderBy) stay out of 0.45 code
dz-v04-legacy-index-apiDZ-V04 · PostgreSQL indexes use the 0.31+ API: ordering and nulls on each column, using(method, …columns)
dz-v05-deprecated-apisDZ-V05/V12 · New code avoids APIs drizzle-orm 0.45 marks deprecated (InferModel, placeholder/param/name, fromList, positional primaryKey…)
dz-v05-lsp-deprecatedDZ-V05 · No new call resolves to a deprecated Drizzle overload (language server: table extra config, keys, conflict targets, aliases)
dz-v05-object-extra-configDZ-V05 · Table extra configuration returns an array (the object form is deprecated in 0.45)
dz-v06-kit-commandsDZ-V06/V07 · drizzle-kit is called with commands and flags of the installed 0.31 CLI (no generate:pg, no 1.0 beta commands or flags)
dz-v08-named-prepareDZ-V08 · Named .prepare() is not used where server-side prepared statements are declared unsupported (postgres-js prepare: false, poolers)
dz-v09-manual-identifier-quotingDZ-V09 · Identifiers and aliases are escaped by Drizzle (sql.identifier, .as), never quoted or escaped by hand
dz-v10-mysql2-modeDZ-V10 · drizzle() for mysql2 gets a relational mode (default or planetscale) whenever it gets a schema
dz-v11-dialect-returningDZ-V11 · MySQL and SingleStore inserts/updates/deletes do not use .returning(); $returningId() returns generated keys only
dz-v12-legacy-rewritesDZ-V12 · Valid older schema syntax is not rewritten without a defect: explicit column names and serial columns stay as written
Configuration 6
Settings read once at a boundary, validated, with units and without unsafe defaults.
dz-c01-kit-configDZ-C01 · drizzle.config keys and values exist in drizzle-kit 0.31 (dialect set and valid, no legacy driver/connectionString, no unknown keys)
dz-c02-unchecked-connection-envDZ-C02 · Runtime clients validate required connection settings at initialization instead of asserting process.env.X!
dz-c03-schema-exportsDZ-C03 · Tables, enums and views in the config's schema files are exported, and new table files are inside the schema globs
dz-c04-relational-schemaDZ-C04 · db.query is used only on a drizzle() instance created with the schema (tables and relations)
dz-c05-casing-consistencyDZ-C05 · The runtime drizzle() casing matches drizzle.config casing, so queries and migrations name columns alike
dz-c08-driver-capabilitiesDZ-C08 · No interactive transaction on drivers that throw on it (neon-http, xata-http): use batch() or a session driver
Dependencies & toolchain 15
Packages change through the package manager, lockfiles stay consistent and the project's tools are used.
dz-t01-hand-written-manifestDZ-T01 · A new package.json without dependencies is initialized with the package manager
dz-t01-new-manifestDZ-T01/T02 · A new package.json comes from the package-manager initializer (or a generator), and gets its dependencies from the package manager
dz-t02-manual-dependency-editDZ-T02 · Dependencies change through the package manager (add/remove/update), never by editing package.json by hand
dz-t02-package-manager-callRecords the tool calls that run a package manager (they may rewrite package.json and lockfiles; DZ-T02/T03)
dz-t02-version-lookupsDZ-T02 · Registry version lookups are not a step before installing
dz-t02-version-pinsDZ-T02 · Packages are installed by name: no guessed exact version, no --save-exact without a reason
dz-t03-hand-edited-lockfileDZ-T03 · Lockfiles are written by the package manager only (no hand edit, no script rewriting them)
dz-t03-resolved-versionDZ-T03 · After an install that touched drizzle packages, the resolved versions are still on this profile's line
dz-t03-stale-lockfileDZ-T03 · A turn that changes package.json dependencies also updates the lockfile next to it (once per stop)
dz-t04-package-managerDZ-T04 · Dependencies are installed with the project's package manager (packageManager field, lockfile)
dz-t04-second-lockfileDZ-T04 · No second lockfile next to another package manager's lockfile
dz-t05-kit-binaryDZ-T05 · drizzle-kit runs from the project (script or local binary), not from a downloaded or global copy
dz-t06-dependency-placementDZ-T06 · drizzle-orm and the driver are runtime dependencies, drizzle-kit a dev dependency; only the selected driver is installed
dz-t10-locked-installsDZ-T10 · CI and container builds restore dependencies from the lockfile (npm ci, --frozen-lockfile, --immutable)
dz-v01-install-specsDZ-V01/V02 · drizzle packages are not installed at a 1.0 beta/RC tag or a version below this profile's line
Generated files 3
Build outputs, generated code and tool-owned files are regenerated, never edited by hand.
dz-m10-migration-ledgerDZ-M10 · The __drizzle_migrations ledger is never written by hand (no synthetic insert, update or delete of applied rows)
dz-t07-hand-transcribed-schemaDZ-T07 · A schema for an existing database is pulled with drizzle-kit, not transcribed by hand from introspection output
dz-t08-migration-metadataDZ-T08 · Migration journals and snapshots are written by drizzle-kit (or git), never by hand or by a script
Completion evidence 10
A turn ends with its checks run and passing; claims of success need proof after the last edit.
dz-git-callRecords the tool calls that run git (checkouts and merges rewrite migration metadata legitimately; DZ-T08)
dz-kit-callRecords drizzle-kit runs: the call (for migration files it writes, DZ-T08/M06) and the turn's ledger of kit verbs (DZ-M07, M13)
dz-kit-ledgerRecords, per turn, the drizzle-kit verbs and migration runs that succeeded (DZ-M07, M13)
dz-m07-up-is-not-migrateDZ-M07 · drizzle-kit up (snapshot format upgrade) is not reported as applying migrations to a database (once per stop)
dz-m12-tool-output-errorsDZ-M12 · push/migrate output reporting an error or an abort is treated as a failure even when the exit code is 0
dz-m13-migration-evidenceDZ-M13/Z02 · A turn that adds migrations shows them applied somewhere (migrate, push to a scratch database, or a test) before ending (once per stop)
dz-turn-baselineRecords the work tree when a prompt starts: the baseline of the Drizzle end-of-turn checks (DZ-T03, M03, M13, Z01)
dz-z01-evidence-ledgerRecords, per session, Claude's edits (file tools and shell writes) and the typecheck, lint and test runs that succeeded after them (DZ-Z01, M13)
dz-z01-new-type-errorsDZ-Z01/A08 · A turn adds no TypeScript errors to the changed files or their importers (language server, once per stop)
dz-z01-required-checksDZ-Z01 · A turn that changed Drizzle code ends only after the configured checks (default: typecheck) succeeded on the final files (once per stop)