Skip to main content
doc0blade47/drizzle-ormdocumentation home
Sign in

ESLint Plugin Drizzle

Last updated Oct 12, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

The eslint-plugin-drizzle package provides static analysis rules for Drizzle ORM to prevent destructive, accidental database modifications during development. It exposes rule modules for enforcing conditional where clauses on both deletion and update operations, protecting against accidental whole-table wipes. Sources: eslint-plugin-drizzle/src/index.ts:9-12, eslint-plugin-drizzle/src/enforce-delete-with-where.ts:16-17, eslint-plugin-drizzle/src/enforce-update-with-where.ts:15-17

The plugin bundles ready-to-use configuration presets, including recommended and all, which register rules such as drizzle/enforce-delete-with-where and drizzle/enforce-update-with-where as compilation errors. Sources: eslint-plugin-drizzle/src/configs/recommended.ts:10-13, eslint-plugin-drizzle/src/configs/all.ts:10-13, eslint-plugin-drizzle/src/index.ts:14

By traversing abstract syntax tree (AST) member expressions and call chains via custom AST resolution utilities, the plugin matches configured database client identifiers and flags query builders missing explicit filter conditions. Sources: eslint-plugin-drizzle/src/enforce-delete-with-where.ts:36-47, eslint-plugin-drizzle/src/enforce-update-with-where.ts:35-54, eslint-plugin-drizzle/src/utils/ast.ts:3-35, eslint-plugin-drizzle/src/utils/options.ts:23-41

Plugin Architecture and Rule Exports

Rule and Configuration Registrations

The eslint-plugin-drizzle entry point exports rules, configuration presets, and package metadata directly from its main module definition. The rules export object registers two specific static analysis rules (enforce-delete-with-where and enforce-update-with-where), typed against TSESLint.RuleModule<string, Options> using TypeScript's satisfies operator. Package version and name details are dynamically imported from package.json into the meta export property.

import type { TSESLint } from '@typescript-eslint/utils';
import { name, version } from '../package.json';
import all from './configs/all';
import recommended from './configs/recommended';
import deleteRule from './enforce-delete-with-where';
import updateRule from './enforce-update-with-where';
import type { Options } from './utils/options';
 
export const rules = {
	'enforce-delete-with-where': deleteRule,
	'enforce-update-with-where': updateRule,
} satisfies Record<string, TSESLint.RuleModule<string, Options>>;
 
export const configs = { all, recommended };
 
export const meta = { name, version };

Sources: eslint-plugin-drizzle/src/index.ts:1-16

Export NameTarget Property / Source ModuleType / Definition
rules'enforce-delete-with-where', 'enforce-update-with-where'Record<string, TSESLint.RuleModule<string, Options>>
configsall, recommendedPresets object containing rulesets
metaname, versionImported from ../package.json

Sources: eslint-plugin-drizzle/src/index.ts:9-16

Note

The rules record satisfies TSESLint.RuleModule<string, Options>, binding rule implementations to custom option types defined in ./utils/options. Sources: eslint-plugin-drizzle/src/index.ts:7-12

Bundled Configuration Presets

Preset Ruleset Configurations

The plugin defines two prepackaged configuration files, recommended.ts and all.ts, which share identical environment and rule settings to enforce query safety constraints. Both configuration exports configure the parser and environment settings for modern JavaScript and TypeScript projects while registering the core safety rules as errors.

export default {
	env: {
		es2024: true,
	},
	parserOptions: {
		ecmaVersion: 'latest',
		sourceType: 'module',
	},
	plugins: ['drizzle'],
	rules: {
		'drizzle/enforce-delete-with-where': 'error',
		'drizzle/enforce-update-with-where': 'error',
	},
};

Sources: eslint-plugin-drizzle/src/configs/recommended.ts:1-14

Configuration PropertyValue / SettingPurpose
env.es2024trueEnables ES2024 global variables and syntax parsing
parserOptions.ecmaVersion'latest'Sets the ECMAScript version to the latest standard
parserOptions.sourceType'module'Treats files as ECMAScript modules
plugins['drizzle']Registers the drizzle plugin namespace
rules['drizzle/enforce-delete-with-where']'error'Flags table deletions lacking a where filter
rules['drizzle/enforce-update-with-where']'error'Flags table updates lacking a where filter

Sources: eslint-plugin-drizzle/src/configs/recommended.ts:1-14, eslint-plugin-drizzle/src/configs/all.ts:1-14

Note

The all.ts preset mirrors recommended.ts completely, establishing identical strict error reporting for both configuration exports across the codebase. Sources: eslint-plugin-drizzle/src/configs/all.ts:1-14

Enforcing WHERE Clauses on Deletions

Static Analysis and Rule Metadata

The enforce-delete-with-where rule uses @typescript-eslint/utils to inspect Abstract Syntax Trees (AST) and prevent accidental execution of unconstrained table deletions eslint-plugin-drizzle/src/enforce-delete-with-where.ts:1-18. Defined via ESLintUtils.RuleCreator, the rule classifies failures under the problem meta type, marks itself as fixable via code, and accepts an options schema restricting configuration to the drizzleObjectName property eslint-plugin-drizzle/src/enforce-delete-with-where.ts:5-33.

const deleteRule = createRule<Options, MessageIds>({
	defaultOptions: [{ drizzleObjectName: [] }],
	name: 'enforce-delete-with-where',
	meta: {
		type: 'problem',
		docs: {
			description: 'Enforce that `delete` method is used with `where` to avoid deleting all the rows in a table.',
		},
		fixable: 'code',
		messages: {
			enforceDeleteWithWhere:
				"Without `.where(...)` you will delete all the rows in a table. If you didn't want to do it, please use `{{ drizzleObjName }}.delete(...).where(...)` instead. Otherwise you can ignore this rule here",
		},
		schema: [{
			type: 'object',
			properties: {
				drizzleObjectName: {
					type: ['string', 'array'],
				},
			},
			additionalProperties: false,
		}],
	},

Sources: eslint-plugin-drizzle/src/enforce-delete-with-where.ts:11-33

Rule Metadata PropertyValue / SettingDescription
name'enforce-delete-with-where'Registered rule identifier
meta.type'problem'Categorizes the rule as detecting potential code errors
meta.fixable'code'Indicates automatic fix capabilities exist
meta.messages.enforceDeleteWithWhere"Without \.where(...)` you will delete all the rows..."`Warning message reported when where is omitted
meta.schemaArray containing option definitionsValidates drizzleObjectName as string or array

Sources: eslint-plugin-drizzle/src/enforce-delete-with-where.ts:11-33

Warning

Omitting .where(...) on a Drizzle delete builder generates an AST match that triggers an error report, preventing accidental truncation of entire database tables eslint-plugin-drizzle/src/enforce-delete-with-where.ts:16-23. Sources: eslint-plugin-drizzle/src/enforce-delete-with-where.ts:16-23

Member Expression Traversal and State Tracking

The rule traversal visitor evaluates MemberExpression nodes to verify whether a .delete call is immediately preceded by a .where method eslint-plugin-drizzle/src/enforce-delete-with-where.ts:34-50. It maintains a module-scoped lastNodeName variable to track the previously visited property identifier in chained method calls eslint-plugin-drizzle/src/enforce-delete-with-where.ts:9, eslint-plugin-drizzle/src/enforce-delete-with-where.ts:47. When node.property.name equals 'delete', lastNodeName is checked to ensure it is not 'where', and isDrizzleObj(node, options) validates that the expression originates from a registered database instance eslint-plugin-drizzle/src/enforce-delete-with-where.ts:37-38.

create(context, options) {
	return {
		MemberExpression: (node) => {
			if (node.property.type === 'Identifier') {
				if (node.property.name === 'delete' && lastNodeName !== 'where' && isDrizzleObj(node, options)) {
					context.report({
						node,
						messageId: 'enforceDeleteWithWhere',
						data: {
							drizzleObjName: resolveMemberExpressionPath(node),
						},
					});
				}
				lastNodeName = node.property.name;
			}
			return;
		},
	};
},

Sources: eslint-plugin-drizzle/src/enforce-delete-with-where.ts:34-52

Note

The module-scoped lastNodeName tracker relies on sequential AST visitor traversal order; if lastNodeName equals 'where', the preceding .delete() call is considered safe eslint-plugin-drizzle/src/enforce-delete-with-where.ts:9, eslint-plugin-drizzle/src/enforce-delete-with-where.ts:38. Sources: eslint-plugin-drizzle/src/enforce-delete-with-where.ts:9, eslint-plugin-drizzle/src/enforce-delete-with-where.ts:38

Enforcing WHERE Clauses on Updates

Rule Metadata and Configuration Schema

The enforce-update-with-where rule checks that database update statements include a .where(...) filter, preventing accidental modification of all rows in a table. It is registered via @typescript-eslint/utils under the name 'enforce-update-with-where' with metadata type 'problem' and fixable status 'code' eslint-plugin-drizzle/src/enforce-update-with-where.ts:1-18.

defaultOptions: [{ drizzleObjectName: [] }],
name: 'enforce-update-with-where',
meta: {
	type: 'problem',
	docs: {
		description: 'Enforce that `update` method is used with `where` to avoid deleting all the rows in a table.',
	},
	fixable: 'code',
	messages: {
		enforceUpdateWithWhere:
			"Without `.where(...)` you will update all the rows in a table. If you didn't want to do it, please use `{{ drizzleObjName }}.update(...).set(...).where(...)` instead. Otherwise you can ignore this rule here",
	},
	schema: [{
		type: 'object',
		properties: {
			drizzleObjectName: {
				type: ['string', 'array'],
			},
		},
		additionalProperties: false,
	}],
},

Sources: eslint-plugin-drizzle/src/enforce-update-with-where.ts:11-32

Rule Metadata PropertyValue / SettingDescription
name'enforce-update-with-where'Registered rule identifier for update clause enforcement
meta.type'problem'Categorizes the rule as detecting potential logic errors
meta.fixable'code'Indicates code correction capabilities
meta.messages.enforceUpdateWithWhere"Without \.where(...)` you will update all the rows..."`Warning message reported when where is omitted from update().set()
meta.schemaArray with drizzleObjectName propertyValidates configuration options as a string or array, disallowing additional properties

Sources: eslint-plugin-drizzle/src/enforce-update-with-where.ts:11-32

Warning

Omitting .where(...) when modifying records via update(...).set(...) triggers an AST violation warning, protecting entire database tables from unfiltered bulk updates eslint-plugin-drizzle/src/enforce-update-with-where.ts:16-21. Sources: eslint-plugin-drizzle/src/enforce-update-with-where.ts:16-21

Member Expression Traversal and State Tracking

The rule listener inspects MemberExpression nodes to verify that .set() calls on an update() builder are preceded by .where() eslint-plugin-drizzle/src/enforce-update-with-where.ts:33-45. It utilizes a module-scoped variable lastNodeName to remember the previous property identifier across visitor executions eslint-plugin-drizzle/src/enforce-update-with-where.ts:8, eslint-plugin-drizzle/src/enforce-update-with-where.ts:54. When the current property is 'set', lastNodeName is verified not to be 'where', and node.object must be a CallExpression whose callee is an update member expression originating from a recognized Drizzle object eslint-plugin-drizzle/src/enforce-update-with-where.ts:37-45.

create(context, options) {
	return {
		MemberExpression: (node) => {
			if (node.property.type === 'Identifier') {
				if (
					lastNodeName !== 'where'
					&& node.property.name === 'set'
					&& node.object.type === 'CallExpression'
					&& node.object.callee.type === 'MemberExpression'
					&& node.object.callee.property.type === 'Identifier'
					&& node.object.callee.property.name === 'update'
					&& isDrizzleObj(node.object.callee, options)
				) {
					context.report({
						node,
						messageId: 'enforceUpdateWithWhere',
						data: {
							drizzleObjName: resolveMemberExpressionPath(node.object.callee),
						},
					});
				}
				lastNodeName = node.property.name;
			}
			return;
		},
	};
},

Sources: eslint-plugin-drizzle/src/enforce-update-with-where.ts:33-59

Note

Unlike enforce-delete-with-where which targets the .delete call directly, enforce-update-with-where targets the .set method node when verifying that .where was not the preceding call in the chain eslint-plugin-drizzle/src/enforce-update-with-where.ts:38-39. Sources: eslint-plugin-drizzle/src/enforce-update-with-where.ts:38-39

AST Member Resolution Utilities

Path Traversal and Drizzle Object Matching

The plugin provides internal utility functions in eslint-plugin-drizzle/src/utils/ast.ts and eslint-plugin-drizzle/src/utils/options.ts to inspect Abstract Syntax Tree (AST) nodes for database instance matching and call-chain resolution. The resolveMemberExpressionPath function traverses a TSESTree.MemberExpression node upward through its object references, accumulating a dot-separated string representation of the call chain eslint-plugin-drizzle/src/utils/ast.ts:3-37.

export const resolveMemberExpressionPath = (node: TSESTree.MemberExpression) => {
	let objectExpression = node.object;
	let fullName = '';
 
	const addToFullName = (name: string) => {
		const prefix = fullName ? '.' : '';
		fullName = `${name}${prefix}${fullName}`;
	};
 
	while (objectExpression) {
		if (objectExpression.type === 'MemberExpression') {
			if (objectExpression.property.type === 'Identifier') {
				addToFullName(objectExpression.property.name);
			}
			objectExpression = objectExpression.object;
		} else if (objectExpression.type === 'CallExpression' && objectExpression.callee.type === 'Identifier') {
			addToFullName(`${objectExpression.callee.name}(...)`);
			break;
		} else if (objectExpression.type === 'CallExpression' && objectExpression.callee.type === 'MemberExpression') {
			if (objectExpression.callee.property.type === 'Identifier') {
				addToFullName(`${objectExpression.callee.property.name}(...)`);
			}
			objectExpression = objectExpression.callee.object;
		} else if (objectExpression.type === 'Identifier') {
			addToFullName(objectExpression.name);
			break;
		} else if (objectExpression.type === 'ThisExpression') {
			addToFullName('this');
			break;
		} else {
			break;
		}
	}
 
	return fullName;

Sources: eslint-plugin-drizzle/src/utils/ast.ts:3-37

Note

During AST traversal, resolveMemberExpressionPath specifically intercepts identifiers, ThisExpression nodes, member expressions, and call expressions, appending (...) syntax to method calls before breaking or continuing path reduction eslint-plugin-drizzle/src/utils/ast.ts:12-35. Sources: eslint-plugin-drizzle/src/utils/ast.ts:12-35]

Configuration and Object Name Validation

Configured database object names are validated against AST nodes using isDrizzleObj and isDrizzleObjName eslint-plugin-drizzle/src/utils/options.ts:3-41. The rule options schema accepts a drizzleObjectName parameter configured as either a single string or an array of strings eslint-plugin-drizzle/src/utils/options.ts:3-5. If the array is empty, all object names are matched by default eslint-plugin-drizzle/src/utils/options.ts:12-15.

export type Options = readonly [{
	drizzleObjectName: string[] | string;
}];
 
const isDrizzleObjName = (name: string, drizzleObjectName: string[] | string) => {
	if (typeof drizzleObjectName === 'string') {
		return name === drizzleObjectName;
	}
 
	if (Array.isArray(drizzleObjectName)) {
		if (drizzleObjectName.length === 0) {
			return true;
		}
 
		return drizzleObjectName.includes(name);
	}
 
	return false;
};

Sources: eslint-plugin-drizzle/src/utils/options.ts:3-21

The isDrizzleObj function evaluates the structure of a MemberExpression node's object property to determine if it originates from a recognized database instance identifier or chained expression eslint-plugin-drizzle/src/utils/options.ts:23-41.

export const isDrizzleObj = (
	node: TSESTree.MemberExpression,
	options: Options,
) => {
	const drizzleObjectName = options[0].drizzleObjectName;
 
	if (node.object.type === 'Identifier') {
		return isDrizzleObjName(node.object.name, drizzleObjectName);
	} else if (node.object.type === 'MemberExpression' && node.object.property.type === 'Identifier') {
		return isDrizzleObjName(node.object.property.name, drizzleObjectName);
	} else if (node.object.type === 'CallExpression') {
		if (node.object.callee.type === 'Identifier') {
			return isDrizzleObjName(node.object.callee.name, drizzleObjectName);
		} else if (node.object.callee.type === 'MemberExpression' && node.object.callee.property.type === 'Identifier') {
			return isDrizzleObjName(node.object.callee.property.name, drizzleObjectName);
		}
	}
 
	return false;

Sources: eslint-plugin-drizzle/src/utils/options.ts:23-41

Tip

Setting drizzleObjectName to an empty array ([]) in rule configurations bypasses strict name filtering, causing the plugin to evaluate query builder methods across all matching member expressions regardless of the base variable name eslint-plugin-drizzle/src/utils/options.ts:12-15. Sources: eslint-plugin-drizzle/src/utils/options.ts:12-15

Was this page helpful?