Relevant source files
The following files were used as context for generating this wiki page:
- eslint-plugin-drizzle/src/enforce-update-with-where.ts
- eslint-plugin-drizzle/src/enforce-delete-with-where.ts
- drizzle-kit/src/sqlgenerator.ts
- eslint-plugin-drizzle/src/configs/all.ts
- drizzle-orm/src/gel-core/dialect.ts
- eslint-plugin-drizzle/src/configs/recommended.ts
- drizzle-kit/src/cli/commands/pgPushUtils.ts
- eslint-plugin-drizzle/src/index.ts
- drizzle-orm/src/mysql-core/dialect.ts
- drizzle-kit/src/serializer/gelSerializer.ts
- eslint-plugin-drizzle/src/utils/options.ts
- drizzle-kit/src/cli/commands/singlestorePushUtils.ts
- .eslintrc.yaml
- eslint-plugin-drizzle/src/utils/ast.ts
- drizzle-orm/src/singlestore-core/dialect.ts
- drizzle-orm/type-tests/mysql/select.ts
- drizzle-orm/type-tests/pg/select.ts
- drizzle-orm/type-tests/mysql/delete.ts
- drizzle-orm/type-tests/geldb/select.ts
- drizzle-orm/type-tests/pg/no-strict-null-checks/test.ts
- drizzle-orm/type-tests/mysql/update.ts
- drizzle-orm/type-tests/geldb/no-strict-null-checks/test.ts
- drizzle-orm/type-tests/mysql/no-strict-null-checks/test.ts
- drizzle-orm/type-tests/sqlite/delete.ts
- drizzle-kit/src/cli/commands/mysqlPushUtils.ts
- drizzle-orm/type-tests/pg/with.ts
- drizzle-orm/type-tests/singlestore/delete.ts
- drizzle-orm/type-tests/sqlite/no-strict-null-checks/test.ts
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 Name | Target Property / Source Module | Type / Definition |
|---|---|---|
rules | 'enforce-delete-with-where', 'enforce-update-with-where' | Record<string, TSESLint.RuleModule<string, Options>> |
configs | all, recommended | Presets object containing rulesets |
meta | name, version | Imported 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 Property | Value / Setting | Purpose |
|---|---|---|
env.es2024 | true | Enables 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 Property | Value / Setting | Description |
|---|---|---|
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.schema | Array containing option definitions | Validates 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 Property | Value / Setting | Description |
|---|---|---|
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.schema | Array with drizzleObjectName property | Validates 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
Related
On This Page
OverviewPlugin Architecture and Rule ExportsRule and Configuration RegistrationsBundled Configuration PresetsPreset Ruleset ConfigurationsEnforcing WHERE Clauses on DeletionsStatic Analysis and Rule MetadataMember Expression Traversal and State TrackingEnforcing WHERE Clauses on UpdatesRule Metadata and Configuration SchemaMember Expression Traversal and State TrackingAST Member Resolution UtilitiesPath Traversal and Drizzle Object MatchingConfiguration and Object Name ValidationRelated