Rules
Runtime guardrails for raw SQL, destructive writes, pagination, locks, and tenant context.
@better-drizzle/rules is the first-party guardrails plugin for Better Drizzle. It validates operations at runtime through plugin hooks and turns unsafe patterns into warnings or errors without forcing you to wrap every repository call manually.
Install
npm install @better-drizzle/rulespnpm add @better-drizzle/rulesyarn add @better-drizzle/rulesbun add @better-drizzle/rulesUsage
import { better } from 'better-drizzle';
import { rules } from '@better-drizzle/rules';
const client = better(db, {
schema,
plugins: [
rules({
noRawUnsafe: true,
noUpdateManyWithoutWhere: true,
requireOrderByForCursor: true,
maxLimit: {
level: 'warn',
value: 500,
},
}),
],
});Rule levels
Every rule accepts four shapes:
true=> errorfalse=> off'warn' | 'error' | 'off'- an object with
levelplus rule-specific options
rules({
noRawUnsafe: true,
requireRawTimeout: {
level: 'error',
maxTimeoutMs: 30_000,
},
maxLimit: {
level: 'warn',
value: 500,
},
});Presets
The package exports three preset helpers:
safe()recommended()strict()
import { recommended, rules } from '@better-drizzle/rules';
const client = better(db, {
schema,
plugins: [
rules(
recommended({
noRawUnsafe: true,
}),
),
],
});What it can enforce
Today the plugin can validate:
- destructive writes without
where - empty
whereclauses - unbounded
findMany()calls - missing or oversized
limitvalues - cursor and pagination order requirements
- include depth and include relation-count limits
- row lock guardrails like
skipLockedprerequisites - raw SQL policies like
noRawUnsafe,requireRawComment,requireRawTimeout,noRawMutation, andnoRawWithoutTransaction - tenant-context checks based on
metaand$withContext(...) - explicit selection of sensitive fields when those fields are visible in
select
Reporting
Warnings can be reported without throwing:
rules({
noRawUnsafe: 'warn',
reporter: {
warn(violation) {
console.warn(violation.rule, violation.operation, violation.model);
},
},
});For error rules, the plugin throws a BetterDrizzleError unless throwOnError: false is configured.
Current boundary
The plugin is intentionally runtime-only and hook-driven. If the current Better Drizzle hook payload does not expose enough information to evaluate a rule safely, that rule is ignored instead of guessed.