Reference
Support matrix
Dialect support for reads, relation loading, writes, upsertMany, locks, raw SQL, transactions, and package compatibility.
This page is the practical compatibility matrix for better-drizzle@0.2.x.
Everything ships in one package, so there is a single version line to track.
| Requirement | Version |
|---|
better-drizzle | 0.2.x |
drizzle-orm peer | ^0.30.0 |
typescript peer | ^5 |
| Node | >=18 |
0.30.0 is the tested floor - 0.29.5 fails the workspace typecheck.
| Entrypoint | Contents | ESM | CJS | Types |
|---|
better-drizzle | better(), definePlugin(), errors, types | Yes | Yes | Yes |
better-drizzle/plugins | plugin authoring API | Yes | Yes | Yes |
better-drizzle/eslint | ESLint plugin (static guardrails) | Yes | Yes | Yes |
better-drizzle/rules | runtime guardrails plugin | Yes | Yes | Yes |
better-drizzle/soft-delete | soft delete plugin | Yes | Yes | Yes |
better-drizzle/timestamps | timestamps plugin | Yes | Yes | Yes |
better-drizzle/zod | Zod schema + validation plugin | Yes | Yes | Yes |
Upgrading from the scoped @better-drizzle/* packages? See the
0.2 upgrade guide.
| Capability | PostgreSQL | SQLite | MySQL |
|---|
findMany, findFirst, findOne, findUnique | Yes | Yes | Yes |
count, exists, paginate, cursor | Yes | Yes | Yes |
create, update, delete, upsert | Yes | Yes | Yes |
createMany, updateMany, deleteMany | Yes | Yes | Yes |
updateEach (single CASE statement) | Yes | Yes | Yes |
repository(name) | Yes | Yes | Yes |
extends(...), $withContext(meta) | Yes | Yes | Yes |
$withState(...), $withoutPlugins() | Yes | Yes | Yes |
| hooks and plugins | Yes | Yes | Yes |
| Capability | PostgreSQL | SQLite | MySQL |
|---|
nested include / select (batched loader) | Yes | Yes | Yes |
| inferred many-to-many through junction tables | Yes | Yes | Yes |
per-parent take / skip (row_number()) | Yes | Yes | Yes |
include._count.select (correlated subqueries) | Yes | Yes | Yes |
relation writes: connect | Yes | Yes | Yes |
relation writes: disconnect, set | Yes | Yes | Yes |
relation loading combined with lock | Rejected | N/A | Rejected |
Relation loading and row locks are mutually exclusive by design - Drizzle's
relational path does not expose lock configuration, so the combination fails
fast instead of silently dropping the lock.
| Capability | PostgreSQL | SQLite | MySQL |
|---|
.explain() on read helpers | Yes | Yes | Yes |
analyze, verbose, costs, timing, summary | Yes | ignored | ignored |
relation stages under deferredRelations | Yes | Yes | Yes |
SQLite uses EXPLAIN QUERY PLAN; MySQL uses the best available EXPLAIN
form. Unsupported flags are reported back under ignoredOptions rather than
silently dropped.
| Capability | PostgreSQL | SQLite | MySQL |
|---|
| typed JSONB scalar paths | Yes | rejected | rejected |
JSONB path mutations (jsonb_set) | Yes | rejected | rejected |
| JSONB full-document replacement | Yes | Yes | Yes |
Typed JSONB filters require jsonb(...).$type<T>() and support scalar dot paths only. For typed path mutations, both dotted paths and the { json: ... } wrapper check declared object paths and values. Untyped columns keep open path names and JSON-encodable values in both forms. Mutations can write object or array values, create absent ancestors, and preserve existing object ancestors and unrelated keys; overlapping paths are rejected. Full-document replacements are not dialect-gated.
| Capability | PostgreSQL | SQLite | MySQL |
|---|
create({ skipDuplicates: true }) | Yes | Yes | dialect-dependent |
createMany({ skipDuplicates: true }) | Yes | Yes | dialect-dependent |
skipDuplicates: ['column'] explicit targets | Yes | Yes | No |
upsertMany | Yes | Yes | No |
upsertMany is intentionally native-first and currently supported only on PostgreSQL and SQLite.
| Capability | PostgreSQL | SQLite | MySQL |
|---|
select on reads | Yes | Yes | Yes |
include on reads | Yes | Yes | Yes |
select / include on single-row writes | Yes | Yes | Yes |
select on upsertMany | Yes | Yes | No support because upsertMany itself is unsupported |
relation include on upsertMany | No | No | No |
| Capability | PostgreSQL | SQLite | MySQL |
|---|
$raw / $executeRaw | Yes | Yes | Yes |
$rawUnsafe with allowUnsafe | Yes | Yes | Yes |
comment support | Best support | limited | limited |
timeoutMs | driver-dependent | driver-dependent | driver-dependent |
| raw hooks | Yes | Yes | Yes |
| Capability | PostgreSQL | SQLite | MySQL |
|---|
transaction() | Yes | Yes | Yes |
| nested transactions / savepoints | Yes | Yes | Yes |
afterCommit / afterRollback | Yes | Yes | Yes |
| retry options | Yes | Yes | Yes |
isolationLevel | Yes | ignored | driver-dependent |
readOnly | Yes | ignored | driver-dependent |
transaction comment | PostgreSQL-oriented | ignored | driver-dependent |
| Capability | PostgreSQL | SQLite | MySQL |
|---|
lock: 'update' / lock: 'share' | Yes | rejected | Yes |
lock: 'noKeyUpdate' / lock: 'keyShare' | Yes | rejected | rejected |
skipLocked / noWait | Yes | N/A | Yes |
tables (lock specific tables) | Yes | rejected | rejected |
locks.transactionsOnly | Yes | Yes | Yes |
| Plugin | PostgreSQL | SQLite | MySQL |
|---|
better-drizzle/rules | Yes | Yes | Yes |
better-drizzle/timestamps | Yes | Yes | Yes |
better-drizzle/soft-delete | Yes | Yes | Yes |
better-drizzle/zod | Yes | Yes | Yes |
better-drizzle/eslint is dialect-independent - it is a static linter, not a
runtime plugin.
When a feature is not safely supported by the current dialect, better-drizzle prefers to fail fast with a structured error instead of silently degrading to a slower or weaker implementation.