eslint-plugin-typeorm-enterprise
v2.3.4
Published
ESLint & oxlint plugin for TypeORM: block raw SQL, prevent SQL injection, enforce transactions, guard multi-tenant queries, and apply enterprise backend governance.
Downloads
845
Maintainers
Readme
🛡️ eslint-plugin-typeorm-enterprise
Stop raw SQL before it reaches production.
A production-ready ESLint plugin that blocks raw SQL execution in TypeORM applications and enforces enterprise backend governance — steering teams toward query builders, repositories, migrations, and safe database abstractions.
Works with any TypeScript version — or none at all (ships compiled JS + a bundled .d.ts):
✨ Why this plugin?
Raw and dynamically-built SQL scattered across a TypeORM codebase is a governance and security liability: it bypasses query builders, invites injection, and fragments data-access patterns across large teams. This plugin catches those patterns at lint time — before review, before merge, before prod — while staying conservative enough to avoid false positives in ordinary request/router code.
| | |
|---|---|
| 🚫 Blocks raw SQL | Static (no-raw-query) and dynamic / injected (require-parameterized-query, no-interpolated-where) SQL |
| 🧨 Guards your data | no-synchronize-true (auto-fixable) and no-unsafe-query-builder-delete stop schema wipes and full-table mutations |
| 🧱 Enforces abstractions | no-entity-manager-query, require-transaction, prefer-transaction-for-multiple-writes keep access in safe layers |
| 🔒 Keeps raw results typed | require-typed-query-result forces a row shape on query() / getRawMany(); no-untyped-record-escape-hatch blocks any and Record<string, any> from standing in for one |
| 🏢 Multi-tenant aware | require-tenant-scope with configurable tenantKeys catches cross-tenant queries |
| 🚀 Performance hints | prefer-exists-over-count steers existence checks away from row counts |
| 🎚️ Config tiers | recommended, warn, strict, performance, multiTenant shareable configs |
| 📦 Dual ESM + CJS | Single TypeScript source compiled to .mjs, .cjs, and .d.ts |
📚 Table of Contents
- Installation
- Quick Start
- Configs
- Rules
- How it works
- For AI coding agents
- Roadmap
- Contributing
- Also by the author
- License
✅ Requirements
- Node
>=18 - ESLint
^9 || ^10(flat config), or oxlint via its JS-plugin API - TypeScript is optional — the package ships compiled JS and bundled types, so it works with any TypeScript version or none at all.
📦 Installation
npm install --save-dev eslint eslint-plugin-typeorm-enterprise🚀 Quick Start
Flat config — eslint.config.js (ESLint 9+)
Extend a shipped config:
const typeormEnterprise = require('eslint-plugin-typeorm-enterprise');
module.exports = [typeormEnterprise.configs.recommended];Or wire rules by hand:
const typeormEnterprise = require('eslint-plugin-typeorm-enterprise');
module.exports = [
{
plugins: { 'typeorm-enterprise': typeormEnterprise },
rules: {
'typeorm-enterprise/no-raw-query': 'error',
'typeorm-enterprise/require-parameterized-query': 'error',
'typeorm-enterprise/no-synchronize-true': 'error',
'typeorm-enterprise/no-entity-manager-query': 'error',
},
},
];Legacy .eslintrc
module.exports = {
plugins: ['typeorm-enterprise'],
rules: {
'typeorm-enterprise/no-raw-query': 'error',
'typeorm-enterprise/require-parameterized-query': 'error',
},
};oxlint (.oxlintrc.json)
Every rule works without type information, so they run under oxlint's JS-plugin API (ESLint v9-compatible, currently alpha) with no adapter:
{
"jsPlugins": ["eslint-plugin-typeorm-enterprise"],
"rules": {
"typeorm-enterprise/no-raw-query": "error",
"typeorm-enterprise/require-parameterized-query": "error",
"typeorm-enterprise/no-unsafe-query-builder-delete": "error",
"typeorm-enterprise/require-typed-query-result": "error",
"typeorm-enterprise/no-untyped-record-escape-hatch": "error"
}
}The plugin's meta.name is typeorm-enterprise, so rule names are identical
across ESLint and oxlint. Shareable configs are an ESLint feature — under oxlint,
enable rules individually as above.
oxlint has no type-aware support, so rules with a typeAware option fall back to
their AST-only behavior there automatically — including
require-typed-query-result and no-untyped-record-escape-hatch, which identify
the receiver by name and read the type the developer wrote. oxlint parses
TypeScript natively, so annotations and type arguments are still seen.
Framework recipes
Copy-paste starters live in examples/:
- NestJS —
nestjs.eslint.config.mjs(strict+performance) - Express / Node —
express.eslint.config.js - Multi-tenant —
multitenant.eslint.config.mjs(customtenantKeys) - oxlint —
examples/.oxlintrc.json
🎚️ Configs
| Config | Severity | Contents |
|---|---|---|
| recommended | error | Broadly-safe rules: raw SQL, injection, schema, EntityManager, unsafe deletes |
| warn | warn | Same rules as recommended, as warnings |
| strict | error | recommended + require-transaction + prefer-transaction-for-multiple-writes + require-typed-query-result + no-untyped-record-escape-hatch |
| recommendedTypeChecked | error | Same rules as recommended, with typeAware: true wherever the rule supports it |
| strictTypeChecked | error | Same rules as strict, with typeAware: true wherever the rule supports it |
| performance | warn | Performance-tuning hints (prefer-exists-over-count) |
| multiTenant | error | recommended + require-tenant-scope |
const typeormEnterprise = require('eslint-plugin-typeorm-enterprise');
module.exports = [
typeormEnterprise.configs.strict, // maximum enforcement
typeormEnterprise.configs.performance, // + perf warnings
// typeormEnterprise.configs.multiTenant, // for multi-tenant apps
];Type-checked configs
Several rules take a typeAware option that confirms the receiver by its
TypeScript type instead of its name — fewer false positives, and TypeORM objects
are caught under any variable name. Setting it per rule is easy to miss, so
recommendedTypeChecked and strictTypeChecked turn it on everywhere it
applies:
import tseslint from 'typescript-eslint';
import typeormEnterprise from 'eslint-plugin-typeorm-enterprise';
export default [
{
files: ['src/**/*.ts'],
languageOptions: {
parser: tseslint.parser,
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
},
},
typeormEnterprise.configs.strictTypeChecked,
];They need the typescript-eslint parser with a
project or projectService. Without it every rule falls back to its AST-only
behavior, so enabling the config ahead of the parser setup is safe — it just
does less.
📏 Rules
| Rule | Description | 🔧 | Config |
|---|---|:--:|:--:|
| no-raw-query | Disallow raw SQL execution through TypeORM query helpers and raw SQL methods. | | ✅ recommended |
| require-parameterized-query | Require parameterized queries instead of interpolated or concatenated raw SQL. | | ✅ recommended |
| no-synchronize-true | Disallow enabling synchronize: true in TypeORM data source configuration. | 🔧 | ✅ recommended |
| no-entity-manager-query | Disallow raw queries executed directly on a TypeORM EntityManager. | | ✅ recommended |
| require-transaction | Require data-mutating operations to run inside a transaction callback. | | ⚠️ strict |
| no-unsafe-query-builder-delete | Disallow QueryBuilder delete/update chains that execute without a where clause. | | ✅ recommended |
| no-interpolated-where | Disallow interpolated or concatenated strings in QueryBuilder where clauses. | | ✅ recommended |
| prefer-transaction-for-multiple-writes | Suggest combining multiple write operations into a single transaction. | | ⚠️ strict |
| require-tenant-scope | Require tenant-scoped access on TypeORM read and write operations (multi-tenant). | | 🏢 multiTenant |
| prefer-exists-over-count | Prefer an existence check over counting rows when only presence matters. | | 🚀 performance |
| require-typed-query-result | Require raw TypeORM query results to be explicitly typed. | | ⚠️ strict |
| no-untyped-record-escape-hatch | Disallow typing raw TypeORM query results with escape hatches such as any, object or Record<string, any>. | | ⚠️ strict |
| require-query-runner-release | Require a QueryRunner to be released in a finally block. | | ✅ recommended |
🔧 = auto-fixable. Full option references live in docs/rules/.
Run npm run doc to regenerate this table from rule metadata.
🧠 How it works
The plugin is written in TypeScript (src/) and compiled with
tsup into a dual ESM + CommonJS bundle plus type
declarations (dist/). Every rule is AST-based and requires no type information
from your project, so it works in any ESLint 9 setup with zero parser config.
Each rule resolves the call's callee (method + object name), applies the
configured allow/restrict lists and ignorePatterns globs, and only then
inspects the relevant argument. The design is deliberately conservative: it
enforces the patterns it can prove and stays quiet on everything else.
🤖 For AI coding agents
This package ships a machine-readable summary following the llms.txt convention — every rule, every config, and the compatibility matrix in ~60 lines, so an agent can configure the plugin correctly without reading this README.
| Where | What |
|---|---|
| https://alokraj68.github.io/eslint-plugin-typeorm-enterprise/llms.txt | Canonical hosted copy — fetchable by any agent with web access |
| node_modules/eslint-plugin-typeorm-enterprise/llms.txt | Same file, shipped in the npm tarball for offline/local agents |
| AGENTS.md | Instructions for agents working in this repository (layout, commands, conventions) |
Using this plugin in your project? Point your agent at the hosted URL, or
add a line to your own AGENTS.md / CLAUDE.md:
Lint rules for TypeORM data access come from eslint-plugin-typeorm-enterprise.
Rule reference: node_modules/eslint-plugin-typeorm-enterprise/llms.txtllms.txt is the source of truth: AGENTS.md is generated from it by
npm run doc:agents, and CI fails on drift.
🗺️ Roadmap
- [x] Twelve rules across SQL safety, schema, transactions, multi-tenancy, result typing, and performance
- [x]
recommended/warn/strict/performance/multiTenantconfigs - [x] TypeScript source with tsup build (dual ESM + CJS)
- [x] CI matrix (Node 18/20/22 · TypeScript 5.5–7) + coverage
- [x] Auto-generated rules table (drift-checked in CI)
- [x] Runs under both ESLint 9+ and oxlint (JS-plugin API)
- [x] npm publish via Trusted Publishing (OIDC) with provenance
- [x] Optional type-aware detection (
typeAware) across all receiver-based rules, with graceful AST-only fallback - [x]
llms.txt+AGENTS.mdfor AI coding agents, published to GitHub Pages - [ ] Autofix suggestions toward Repository / QueryBuilder APIs
- [ ] Documentation site / playground
🤝 Contributing
git clone https://github.com/alokraj68/eslint-plugin-typeorm-enterprise.git
cd eslint-plugin-typeorm-enterprise
npm install
npm run ci # lint + typecheck + build + testHandy scripts:
| Script | Does |
|---|---|
| npm run build | Compile src/ → dist/ (ESM + CJS + d.ts) |
| npm run lint | ESLint (typescript-eslint) over src/ |
| npm run typecheck | tsc --noEmit |
| npm test | Build, then run the rule test suites |
| npm run coverage | Test with coverage report |
Every push and PR runs the CI matrix; merges to main auto-publish when the
package.json version bumps. Please read the
Code of Conduct
and Contributing guide.
🧰 Also by the author
Same working principle as this plugin: if a rule matters, it fails the build rather than living in a checklist someone is trusted to follow.
craftkit is four of them for Claude Code, installed in one command:
npx @alokraj68/craftkit| | | |
|---|---|---|
| ✍️ plainspoken | docs | prose that does not read as machine-written |
| 📱 pagecheck | docs | pages that survive a phone: overflow, tiny text, tap targets, WCAG AA |
| 📄 ats-resume | docs | a résumé an applicant tracking system can parse, and JD gap analysis |
| 🧭 craft-setup | skill only | verify before claiming done; never commit unasked |
🌐 alokraj68.in — who writes these, and what they were built for.
📄 License
MIT © alokraj68
