Profiles·Public

drizzle-orm

semver>=0.45.0 <1.0.0postconditions15functions9last verified2026-06-12coverage score69%

Postconditions — what we check

  • select · select-query-error
    error
    Whenquery fails (connection lost, timeout, invalid column, SQL syntax error)
    ThrowsError with database-specific error details
    Required handlingCaller MUST wrap select queries in try-catch to handle database errors. Connection failures and SQL errors crash application if unhandled.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[1]
  • insert · insert-constraint-violation
    error
    Wheninsert violates constraint (unique, foreign key, not null, check constraint)
    ThrowsError with constraint violation details and database error code
    Required handlingCaller MUST wrap insert operations in try-catch to handle constraint violations. Unique violations, foreign key errors, and validation failures crash application if unhandled.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[2]
  • update · update-constraint-violation
    error
    Whenupdate violates constraint or fails (unique, foreign key, connection lost)
    ThrowsError with constraint violation or connection error details
    Required handlingCaller MUST wrap update operations in try-catch to handle constraint violations and connection errors. Database errors crash application if unhandled.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[3]
  • delete · delete-constraint-violation
    error
    Whendelete violates foreign key constraint (referenced by other tables)
    ThrowsError with foreign key constraint violation details
    Required handlingCaller MUST wrap delete operations in try-catch to handle foreign key constraint violations. Cascade deletes and constraint errors crash application if unhandled.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[4]
  • transaction · transaction-rollback-error
    error
    Whentx.rollback() is called inside the transaction callback
    ThrowsTransactionRollbackError (extends DrizzleError) with message 'Rollback'
    Required handlingCaller wrapping db.transaction() MUST catch TransactionRollbackError to distinguish intentional rollbacks (business logic rejected the transaction) from actual database failures. If TransactionRollbackError is swallowed silently, the caller has no way to tell whether the transaction committed or was intentionally aborted. Pattern: catch (err) { if (err instanceof TransactionRollbackError) { /* intentional */ } else { throw err; } }
    costhighin prodsilent failureusers seelost datavisibilitysilent
    Sources[5][6]
  • transaction · transaction-db-error-no-try-catch
    error
    Whendb.transaction() called without try-catch, and an operation inside throws
    ThrowsDatabase-specific error (e.g. PostgreSQL error code 23505, 23503, ECONNRESET) that propagates through transaction() after automatic rollback
    Required handlingCaller MUST wrap db.transaction() in try-catch. When any operation inside the callback throws, drizzle automatically rolls back the entire transaction and re-throws the original error. Without try-catch at the call site, the error propagates as an unhandled rejection, crashing the application while the transaction is correctly rolled back — but the caller has no chance to retry or return a meaningful error to the user.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[5]
  • transaction · transaction-nested-savepoint
    warning
    Whendb.transaction() called inside another transaction (nested transaction via PgTransaction.transaction())
    ThrowsUses SAVEPOINT semantics — inner rollback only affects savepoint, not outer transaction; outer tx.rollback() affects everything
    Required handlingCaller using nested transactions MUST understand savepoint semantics. A rollback in an inner transaction only rolls back to the savepoint, not the entire outer transaction. The outer transaction continues and MUST be explicitly committed or rolled back. Missing this distinction causes partial data writes that appear as correct commits to the application but only committed a subset of the intended work.
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[5]
  • execute · execute-query-error
    error
    Whenraw SQL fails — syntax error, table not found, type mismatch, constraint violation
    ThrowsDrizzleQueryError with message 'Failed query: <sql>\nparams: <params>', wrapping the original driver error as .cause
    Required handlingCaller MUST wrap db.execute() in try-catch. DrizzleQueryError includes the executed query and params in the error message — NEVER log these unredacted to external services (they may contain PII). Check err.cause for the underlying PostgreSQL error code. Common codes: 42601 (syntax error), 42P01 (table not found), 23505 (unique violation), 23503 (foreign key violation).
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[7][6]
  • execute · execute-type-mismatch-no-runtime-check
    warning
    Whensql<T> generic parameter does not match actual result shape at runtime
    ThrowsNo error thrown — TypeScript type is a lie; runtime data is wrong shape silently
    Required handlingCaller MUST NOT rely on TypeScript generic type T for runtime validation. Drizzle documentation explicitly states: 'sql<T> does not perform any runtime mapping.' If the SQL result does not match the expected shape, TypeScript will not catch this at runtime — you will get silent type mismatches causing undefined property access or wrong data returned to users. Validate with zod or manual shape checks after db.execute().
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[7]
  • findFirst · findfirst-undefined-on-missing-row
    error
    Whendb.query.<table>.findFirst({ where: ... }) is called and the filter matches zero rows. The promise resolves with `undefined`, not a thrown error.
    ThrowsNo error thrown — returns undefined when no row matches the filter
    Required handlingCaller MUST check the result for undefined before accessing any property. findFirst() returns `T | undefined` — the TypeScript type is honest, but a missing row looks identical to a successful lookup at runtime. Common anti-pattern: const user = await db.query.users.findFirst({ where: eq(users.id, id) }); return user.email; // TypeError if user is undefined Correct pattern: const user = await db.query.users.findFirst({ where: eq(users.id, id) }); if (!user) throw new NotFoundError(`User ${id} not found`); return user.email; This is the most common drizzle bug pattern in production — it surfaces as `Cannot read properties of undefined (reading 'X')` in request logs, usually triggered by stale IDs in URL params, deleted records, or permission-filtered lookups.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][9]
  • findFirst · findfirst-driver-error
    error
    Whendb.query.<table>.findFirst() is called and the underlying driver throws — connection lost (ECONNRESET, ETIMEDOUT), table missing (42P01), invalid relation config (the schema's `relations` object references a column that does not exist on the target table), or query syntax error in the generated SQL.
    ThrowsDriver error (pg.DatabaseError / postgres.PostgresError / etc.) propagates as a Promise rejection
    Required handlingCaller MUST wrap db.query.<table>.findFirst() in try-catch. Connection failures during read paths crash the request handler. Invalid relations configs (a `with: { posts: true }` referencing a relation that was not declared in the schema) surface here at runtime, not at compile time — these are common after schema refactors where relations were removed but query call sites were not updated. The same try-catch should handle both the driver error and (separately) the undefined-row case covered by findfirst-undefined-on-missing-row.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • findMany · findmany-driver-error
    error
    Whendb.query.<table>.findMany() is called and the underlying driver throws — connection lost, table missing, invalid relation config in the `with:` option referencing an undefined relation, or query syntax error.
    ThrowsDriver error (pg.DatabaseError / postgres.PostgresError / etc.) propagates as a Promise rejection
    Required handlingCaller MUST wrap db.query.<table>.findMany() in try-catch. Connection failures during list endpoints (paginated reads, dashboard data loads) crash the request handler. Invalid `with:` configs that reference removed/renamed relations surface here at runtime — they do not produce TypeScript errors because the schema's relations object is checked structurally and a missing key falls back to `unknown`. The error message includes the SQL fragment but NOT the variable name in the application code, so the call site is hard to locate without a stack trace.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • findMany · findmany-empty-array-on-stale-filter
    warning
    Whendb.query.<table>.findMany() is called with a `where:` filter that no rows satisfy (deleted records, wrong organization scope, stale enum value after a schema migration, etc.). The promise resolves with an empty array.
    ThrowsNo error thrown — returns [] silently when filter matches zero rows
    Required handlingCaller SHOULD distinguish empty-result-by-design from empty-result-by-mistake. For endpoints that paginate user data, an empty array is a valid response. For endpoints that expect at least one row (e.g. fetching an organization's billing records during a renewal job), the empty array is a silent indicator of misconfigured filters or stale state and MUST be checked: const rows = await db.query.invoices.findMany({ where: eq(invoices.orgId, orgId) }); if (rows.length === 0) { logger.warn('No invoices found for org', { orgId }); // decide: throw, fallback, or return early } Without this check, downstream code that aggregates or averages over the array silently produces zeros, NaN, or misleading dashboard metrics — a common drizzle production-data bug.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[8]
  • batch · batch-statement-failure-rolls-back-all
    error
    Whendb.batch([...]) is called and any statement in the batch fails (constraint violation, syntax error, connection drop mid-batch). The driver throws — the entire batch is rolled back, none of the statements committed.
    ThrowsDriver-specific error from the failing statement (D1Error, LibsqlError, or NeonDbError). The error message names the failing statement but the typed BatchResponse you expected to receive is NEVER returned — the rejection replaces it.
    Required handlingCaller MUST wrap db.batch() in try-catch. The atomic semantics mean either all statements commit and you get the typed BatchResponse, or NONE commit and you get a rejection. Application code that builds on partial success (e.g. "if step 3 fails, statements 1 and 2 should still be applied") MUST NOT use batch() — use sequential awaits inside individual try-catch blocks instead. Common silent-failure pattern: developers assume batch() returns a per-statement result object indicating which statements succeeded. It does NOT — the success path returns one typed tuple, the failure path throws. Code that destructures the response without try-catch crashes the entire request handler on any single-row constraint violation buried in the batch.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[10]
  • batch · batch-unsupported-on-non-batchable-driver
    warning
    Whendb.batch([...]) is called on a driver wrapper that does not implement batch — node-postgres (drizzle-orm/node-postgres), postgres-js (drizzle-orm/postgres-js), better-sqlite3, mysql2, planetscale. This occurs when shared code receives a db instance typed as a union or `any`, and the user passed the wrong driver.
    ThrowsTypeError: db.batch is not a function (runtime) — TypeScript catches it if the type is precise, but `any`-typed code reaches runtime
    Required handlingCode that uses db.batch() MUST be invoked only with a D1, LibSQL, or Neon db instance. When writing shared utility functions, either parameterize the db type precisely (DrizzleD1Database, LibsqlDatabase, NeonHttpDatabase) or feature-detect: if (typeof db.batch === 'function') { await db.batch([...]); } else { // fallback to db.transaction() for pg/sqlite drivers } A surprising number of drizzle batch() crashes in production come from code that was written assuming D1 in dev/preview environments but ran against postgres-js in production.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]

Sources

Every postcondition cites at least one of these. Numbered to match the footnotes above.

  1. [1]orm.drizzle.team/docs/selecthttps://orm.drizzle.team/docs/select
  2. [2]orm.drizzle.team/docs/inserthttps://orm.drizzle.team/docs/insert
  3. [3]orm.drizzle.team/docs/updatehttps://orm.drizzle.team/docs/update
  4. [4]orm.drizzle.team/docs/deletehttps://orm.drizzle.team/docs/delete
  5. [5]orm.drizzle.team/docs/transactionshttps://orm.drizzle.team/docs/transactions
  6. [6]raw.githubusercontent.com/drizzle-team/drizzle-ormhttps://raw.githubusercontent.com/drizzle-team/drizzle-orm/main/drizzle-orm/src/errors.ts
  7. [7]orm.drizzle.team/docs/sqlhttps://orm.drizzle.team/docs/sql
  8. [8]orm.drizzle.team/docs/rqbhttps://orm.drizzle.team/docs/rqb
  9. [9]raw.githubusercontent.com/drizzle-team/drizzle-ormhttps://raw.githubusercontent.com/drizzle-team/drizzle-orm/main/drizzle-orm/src/pg-core/query-builders/query.ts
  10. [10]orm.drizzle.team/docs/batch-apihttps://orm.drizzle.team/docs/batch-api
Need a different package?
Request a profile