better-drizzle
Reference

Client

better() - options, client-level methods, and the values exported from better-drizzle.

import { better } from 'better-drizzle';

const client = better(db, options);

better(db, options) wraps a Drizzle database instance and returns a fully typed client.

Options

Prop

Type

relations options

better-drizzle infers a direct many-to-many relation whenever a junction table has exactly two required foreign-key relations and no other required columns. Use these options when your schema has more than one possible path.

Prop

Type

const client = better(db, {
	schema,
	relations: {
		manyToMany: [
			{
				through: 'memberships',
				left: { relation: 'user' },
				right: { relation: 'group', name: 'groups' },
			},
		],
	},
});

// `groups` is now a first-class relation on the user delegate
const user = await client.users.findFirst({
	where: { id: 1 },
	include: { groups: { take: 10 } },
});

Ambiguous paths fail with a structured error at use time instead of picking a junction silently. See relations for the inference rules.

raw options

Prop

Type

When raw.enabled is false, every raw method throws a BetterDrizzleError with code RAW_DISABLED. Calling $rawUnsafe() without raw.allowUnsafe: true throws RAW_UNSAFE_DISABLED. With raw.requireComment, a call without options.comment throws RAW_COMMENT_REQUIRED. See error codes.

Client methods

Beyond the per-table delegates, the client exposes:

MethodDescription
client.<table>the model delegate for each table
repository(name)resolve a delegate by schema key or DB table name
extends(objectOrFactory)attach app-specific helpers or values to the client
transaction(fn, options?)run fn in a transaction
$raw(...)run a safe raw read, returns rows
$executeRaw(...)run a safe raw write, returns { rowsAffected }
$rawUnsafe(query, params?, options?)run a raw string query (requires raw.allowUnsafe)
$withContext(meta)clone the client with default metadata merged into every operation

repository(name) accepts either the TypeScript schema key or the underlying database table name:

const usersByKey = client.repository('users');
const usersByDbName = client.repository('app_users');

extends(...) accepts either a plain object or a callback that receives the bound client:

const extended = client.extends((client) => ({
	findById(id: number) {
		return client.users.findFirst({ where: { id } });
	},
}));

See the full guide at Client extensions.

$rawUnsafe

$rawUnsafe(query, params?, options?) takes a plain SQL string. Use ? placeholders for values: each one is replaced by the matching entry of params as a bound parameter, in order. It returns rows like $raw.

const client = better(db, { schema, raw: { allowUnsafe: true } });

const rows = await client.$rawUnsafe(
	'select id, name from users where id = ? and active = ?',
	[1, true],
	{ name: 'admin-user-lookup' },
);

When params is non-empty and the number of ? placeholders differs from params.length, the call throws RAW_UNSAFE_PLACEHOLDER_MISMATCH (status 400) with details: { params, placeholders }. Without params, the string runs as-is.

Transaction clients

The client passed to a transaction(...) callback also has rollback(reason?), afterCommit(cb), and afterRollback(cb). See transactions. The root client does not declare afterCommit / afterRollback; calling them on it at runtime throws AFTER_COMMIT_OUTSIDE_TRANSACTION / AFTER_ROLLBACK_OUTSIDE_TRANSACTION.

Exports

import {
	better,
	definePlugin,
	OrderType,
	version,
	// error helpers
	isDatabaseError,
	getDatabaseErrorInfo,
	isUniqueViolation,
	isForeignKeyViolation,
	isNotNullViolation,
	isCheckViolation,
	// errors
	BetterDrizzleError,
	BetterDrizzleErrorCode,
	BetterDrizzleTransactionRollbackError,
} from 'better-drizzle';

Enums

enum OrderType {
	Asc = 'asc',
	Desc = 'desc',
}

The library also exports its full type surface (BetterDrizzleClient, BetterDrizzleModelDelegate, QueryArgs, WhereInput, Plugin, hook context types, and more) for typing your own helpers.

Every entrypoint re-exports the package version. Since 0.2.0 ships as a single package, these are always the same value:

import { version } from 'better-drizzle';
import { version as softDeleteVersion } from 'better-drizzle/soft-delete';

version === softDeleteVersion; // true

On this page