better-drizzle
Plugins

ESLint

Static Better Drizzle guardrails for IDE and CI feedback.

better-drizzle/eslint is the static surface for Better Drizzle guardrails. It mirrors the directly-checkable subset of better-drizzle/rules and is meant for editor feedback and CI, not runtime enforcement.

Install

npm install -D eslint @typescript-eslint/parser better-drizzle
pnpm add -D eslint @typescript-eslint/parser better-drizzle
yarn add -D eslint @typescript-eslint/parser better-drizzle
bun add -d eslint @typescript-eslint/parser better-drizzle

Flat Config

import parser from '@typescript-eslint/parser';
import betterDrizzle from 'better-drizzle/eslint';

export default [
	{
		files: ['**/*.{ts,tsx,mts,cts}'],
		languageOptions: {
			parser,
			sourceType: 'module',
		},
		plugins: {
			'better-drizzle': betterDrizzle,
		},
		rules: {
			...betterDrizzle.configs.recommended[0].rules,
		},
	},
];

The package also exports safe, recommended, and strict flat-config arrays.

What It Checks

The ESLint plugin only analyzes direct and obvious Better Drizzle call sites such as:

  • client.users.findMany(...)
  • client.repository('users').updateMany(...)
  • client.$rawUnsafe(...)
  • client.users.$withoutPlugins().findMany(...)

It does not try to infer transaction state, runtime meta, dialect-specific behavior, or complex wrapper functions.

Rules

Rule IDs use the better-drizzle/ prefix. The preset columns show the level each preset sets, with its options where they matter.

Destructive writes

RuleReportssaferecommendedstrict
better-drizzle/no-delete-many-without-wheredeleteMany without whereerrorerrorerror
better-drizzle/no-update-many-without-whereupdateMany without whereerrorerrorerror
better-drizzle/no-delete-without-wheredelete without whereerrorerrorerror
better-drizzle/no-update-without-whereupdate without whereerrorerrorerror
better-drizzle/no-empty-wherean empty where on the listed operationserrorerrorerror

Reads and pagination

RuleReportssaferecommendedstrict
better-drizzle/no-unbounded-find-manyfindMany with no limit or take (safe and recommended also accept a where)warnwarnerror
better-drizzle/require-explicit-limitlisted operations with no limit, take, perPage, first, or lastoffoffwarn
better-drizzle/max-limita literal limit above valuewarn (1000)warn (1000)error (500)
better-drizzle/require-order-by-for-limita limit without orderByoffoffwarn
better-drizzle/require-order-by-for-paginationpaginate without orderByoffoffwarn
better-drizzle/require-order-by-for-cursorcursor without orderByerrorerrorerror
better-drizzle/require-stable-order-by-for-cursorcursor whose orderBy lacks id, only when requirePrimaryKeyInOrderBy: trueoffwarnerror

Includes and locks

RuleReportssaferecommendedstrict
better-drizzle/max-include-depthinclude nested deeper than valueoffwarn (3)error (2)
better-drizzle/max-include-relationsmore include relations than valueoffwarn (5)error (3)
better-drizzle/no-lock-with-includelock together with includeerrorerrorerror
better-drizzle/no-invalid-lock-combinationskipLocked together with noWaiterrorerrorerror
better-drizzle/require-order-by-for-skip-lockedskipLocked without orderByoffwarnerror
better-drizzle/require-limit-for-skip-lockedskipLocked without a limiterrorerrorerror

Raw SQL

RuleReportssaferecommendedstrict
better-drizzle/no-raw-unsafeany $rawUnsafe(...) callerrorerrorerror
better-drizzle/require-raw-commenta raw call without a comment of at least minLength charactersoffoffwarn (8)
better-drizzle/require-raw-timeouta raw call without timeoutMs, or above defaultTimeoutMs / maxTimeoutMsoffwarnerror
better-drizzle/no-raw-mutationraw SQL starting with a write verb, unless it starts with an allow prefixoffwarnerror

Sensitive fields and plugin bypass

RuleReportssaferecommendedstrict
better-drizzle/no-sensitive-selectselect with a sensitive field set to true (the presets allow it when the call passes withSensitive: true)offerrorerror
better-drizzle/require-explicit-sensitive-access-reasona sensitive select with withSensitive: true but no reason stringoffofferror
better-drizzle/no-without-pluginsany $withoutPlugins() calloffwarnwarn
better-drizzle/no-plugin-bypass-without-reasona $withoutPlugins() call whose args have no reason stringoffwarnerror

The sensitive field list is fixed: apiKey, apiKeys, password, passwordHash, secret, secrets, ssn, token, tokens. Only literal true values in an inline select object are matched.

Configuring rules

In an ESLint rules block, use standard ESLint severities: 'off', 'warn', 'error' (or 0, 1, 2), or [severity, options]. The true / false and { level } shorthands belong to the runtime better-drizzle/rules plugin and do not work here.

import betterDrizzle from 'better-drizzle/eslint';

export default [
	...betterDrizzle.configs.recommended,
	{
		rules: {
			'better-drizzle/no-without-plugins': 'off',
			'better-drizzle/max-limit': ['error', { value: 200 }],
			'better-drizzle/no-raw-mutation': ['warn', { allow: ['refresh materialized view'] }],
		},
	},
];

Rule options:

RuleOptions
no-empty-whereoperations, treatUndefinedAsEmpty, treatEmptyAndOrAsEmpty
no-unbounded-find-manyallowWithWhere, allowWithLimit, allowWithTake, allowSmallStaticModels
require-explicit-limitoperations
max-limitvalue, applyTo
require-stable-order-by-for-cursorrequirePrimaryKeyInOrderBy
max-include-depth / max-include-relationsvalue
require-raw-commentminLength
require-raw-timeoutdefaultTimeoutMs, maxTimeoutMs
no-raw-mutationallow
no-sensitive-selectallowWithSensitive, withSensitiveArg
require-explicit-sensitive-access-reasonwithSensitiveArg, reasonArg
no-plugin-bypass-without-reasonreasonArg

The other rules take no options.

Presets

safe, recommended, and strict are flat-config arrays, also available as configs.safe, configs.recommended, and configs.strict. Each one sets the TypeScript parser for **/*.{ts,tsx,mts,cts}, registers the plugin, and enables the levels above.

They are generated from the runtime presets of better-drizzle/rules, limited to the statically checkable rules. Runtime-only rules such as tenant context, transaction state, and dialect handling stay in better-drizzle/rules.

Example

import { recommended } from 'better-drizzle/eslint';

export default [
	...recommended,
];

Boundary

Use better-drizzle/eslint when you want immediate feedback in Zed, VS Code, or CI. Use better-drizzle/rules when you need runtime enforcement inside the actual Better Drizzle client.

On this page