Biome linter plugin for Drizzle ORM safety rules. Enforces .where() clauses on delete and update operations to prevent accidental data loss.
This plugin is a port of eslint-plugin-drizzle for the Biome toolchain.
- enforce-delete-with-where: Catches
.delete()calls without.where()that would delete all rows - enforce-update-with-where: Catches
.update().set()calls without.where()that would update all rows - Object name filtering: Optionally restrict rules to specific Drizzle object names (e.g.,
db,tx) - CLI generator: Generate customized plugin configurations
npm install -D biome-plugin-drizzle @biomejs/biome# Initialize with default settings (broad matching)
npx biome-plugin-drizzle init
# Or initialize with object name filtering (reduces false positives)
npx biome-plugin-drizzle init --object-names db,txAdd the plugin to your biome.json:
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"plugins": ["./node_modules/biome-plugin-drizzle/dist/drizzle.grit"],
"linter": {
"enabled": true
}
}Note: This plugin requires Biome 2.0.0 or later, which introduced support for GritQL plugins.
Then run Biome:
npx biome lint .The default plugin matches any .delete() or .update().set() call, which may cause false positives if you have other classes with similar method names.
{
"plugins": ["./node_modules/biome-plugin-drizzle/dist/drizzle.grit"]
}To reduce false positives, generate a customized plugin that only matches specific object names:
# Generate a plugin that only matches 'db' and 'tx' objects
npx biome-plugin-drizzle generate --out ./.biome/drizzle.grit --object-names db,txThen update your biome.json:
{
"plugins": ["./.biome/drizzle.grit"]
}This is equivalent to the ESLint plugin's drizzleObjectName option:
// ESLint equivalent
{
"drizzle/enforce-delete-with-where": ["error", { "drizzleObjectName": ["db", "tx"] }]
}Ensures all .delete() operations include a .where() clause to prevent accidental deletion of entire tables.
// Will delete ALL rows in the users table!
db.delete(users);
// Still deletes all rows (returning doesn't add safety)
db.delete(users).returning();
// Async operations without where
await db.delete(posts);// Safe: has where clause
db.delete(users).where(eq(users.id, 1));
// Safe: where can be anywhere in the chain
db.delete(users).returning().where(eq(users.id, 1));
// Safe: with CTEs
db.with(someCte).delete(users).where(eq(users.id, 1));Ensures all .update().set() operations include a .where() clause to prevent accidental updates to entire tables.
// Will update ALL rows in the users table!
db.update(users).set({ name: "John" });
// Still updates all rows
db.update(users).set({ status: "active" }).returning();
// Async operations without where
await db.update(posts).set({ views: 0 });// Safe: has where clause
db.update(users).set({ name: "John" }).where(eq(users.id, 1));
// Safe: where can be anywhere in the chain
db.update(users).set({ email: "new@email.com" }).returning().where(eq(users.id, 1));
// Safe: complex conditions
db.update(users)
.set({ verified: true })
.where(and(eq(users.status, "pending"), gt(users.age, 18)));Initialize biome-plugin-drizzle in your project.
npx biome-plugin-drizzle init [options]Options:
-c, --config <path>: Path to biome.json (default:./biome.json)--object-names <names>: Comma-separated list of Drizzle object names--out <path>: Output path for generated .grit file (default:./.biome/drizzle.grit)
Generate a customized .grit plugin file.
npx biome-plugin-drizzle generate --out <path> [options]Options:
-o, --out <path>: Output path for the generated .grit file (required)--object-names <names>: Comma-separated list of Drizzle object names--no-delete-rule: Exclude the enforce-delete-with-where rule--no-update-rule: Exclude the enforce-update-with-where rule
Print the path to the installed default plugin.
npx biome-plugin-drizzle print-path [--absolute]Options:
--absolute: Print the absolute path instead of relative
- Biome linter plugins are currently GritQL-based and can only report diagnostics
- Unlike ESLint, Biome plugins cannot accept configuration options in
biome.json - Workaround: Use the CLI generator to create customized plugins with object name filtering
- The plugin uses structural pattern matching, which may not catch all edge cases
- Complex dynamic code patterns may not be detected
- When in doubt, add explicit
.where()clauses to your queries
- Update version in
package.json - Update CHANGELOG if you have one
- Build and test:
npm run build npm test - Publish to npm:
npm publish
Contributions are welcome! Please feel free to submit issues and pull requests.
MIT
Inspired by eslint-plugin-drizzle from the Drizzle Team.