ESLint Plugin for Safe Kysely Operations
raw JSON →eslint-plugin-safe-kysely is an ESLint plugin designed to improve data safety in applications utilizing the Kysely query builder. Currently at version 1.0.1, this plugin enforces the inclusion of a `where` clause in all `updateTable` and `deleteFrom` Kysely call chains. Its primary goal is to prevent accidental mass data modification or deletion of entire tables by requiring explicit targeting of rows for such operations. Unlike runtime checks, this plugin performs static analysis during development, providing immediate feedback through ESLint warnings or errors before code is deployed. This ensures that potentially destructive database queries are identified and corrected early in the development lifecycle, significantly reducing the risk of data loss. While a specific release cadence is not yet established for this relatively new plugin, its initial stable release indicates a commitment to Kysely ecosystem safety. It differentiates itself by offering Kysely-specific static analysis for a critical aspect of database interaction.
Common errors
error ESLint: Definition for rule 'safe-kysely/enforce-where-clause' was not found ↓
npm install eslint-plugin-safe-kysely --save-dev and ensure "safe-kysely" is added to the plugins array in your .eslintrc.json or .eslintrc.js file. error Missing `where` clause with updateTable (or Missing `where` clause with deleteFrom) ↓
.where() method call to your Kysely query chain to specify the rows to be affected, e.g., .where('id', '=', 1) or .where({ foo: 'bar' }). Warnings
gotcha Forgetting to add `safe-kysely` to the `plugins` array in your ESLint configuration will prevent the rule from being recognized or applied. ↓
gotcha When enabling rules from `eslint-plugin-safe-kysely`, you must prefix the rule name with `safe-kysely/`. Omitting this prefix (e.g., just `enforce-where-clause`) will result in ESLint not finding the rule. ↓
gotcha This plugin specifically targets `updateTable` and `deleteFrom` methods for `where` clause enforcement. It does not provide checks for other potentially unsafe Kysely operations, such as raw SQL queries or complex builder patterns outside of its defined scope. ↓
Install
npm install eslint-plugin-safe-kysely yarn add eslint-plugin-safe-kysely pnpm add eslint-plugin-safe-kysely Imports
- Plugin Registration (JSON Config)
{ "plugins": ["safe-kysely"], "rules": { "safe-kysely/enforce-where-clause": "error" } } - Plugin Object (JavaScript Config) wrong
import safeKyselyPlugin from 'eslint-plugin-safe-kysely'; // CommonJS 'require' is typically used for .eslintrc.jscorrectconst safeKyselyPlugin = require('eslint-plugin-safe-kysely'); module.exports = { plugins: [safeKyselyPlugin], rules: { "safe-kysely/enforce-where-clause": "error" } }; - Rule Configuration (Specific Options) wrong
"enforce-where-clause": "error" // Missing plugin name prefixcorrect"safe-kysely/enforce-where-clause": ["error", { /* optional specific rule settings */ }]
Quickstart
npm install eslint --save-dev
npm install eslint-plugin-safe-kysely --save-dev
# .eslintrc.json
{
"env": {
"node": true,
"es2021": true
},
"extends": [
"eslint:recommended"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"safe-kysely"
],
"rules": {
"safe-kysely/enforce-where-clause": "error"
}
}
// example.ts (assuming Kysely and database client setup)
import { Kysely, PostgresDialect } from 'kysely';
import { Pool } from 'pg';
interface Database {
person: {
id: string;
first_name: string;
last_name: string;
age: number;
};
}
const db = new Kysely<Database>({
dialect: new PostgresDialect({
pool: new Pool({
connectionString: process.env.DATABASE_URL ?? 'postgresql://user:password@host:port/database',
}),
}),
});
async function updatePersonAge(id: string, newAge: number) {
// This operation is valid as it includes a .where() clause
await db
.updateTable('person')
.set({ age: newAge })
.where('id', '=', id)
.execute();
console.log(`Person ${id} age updated to ${newAge}.`);
}
// Example of an invalid operation (would be flagged by ESLint)
// await db.updateTable('person').set({ age: 0 }).execute();
updatePersonAge('123', 30);