Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Use parameterized raw queries in ORMs; avoid the *Unsafe escape hatches with input
Raw/escape-hatch ORM methods must receive bindings, not interpolated input — prefer $queryRaw tagged templates over $queryRawUnsafe.
Bad example
| 1 | // Prisma |
| 2 | const rows = await prisma.$queryRawUnsafe( |
| 3 | `SELECT * FROM posts WHERE author = '${authorId}' ORDER BY created DESC`, |
| 4 | ); |
| 5 |
|
| 6 | // Knex |
| 7 | const users = await knex.raw(`SELECT * FROM users WHERE name = '${name}'`); |
| 8 |
|
| 9 | // TypeORM |
| 10 | await dataSource.query(`DELETE FROM sessions WHERE token = '${token}'`); |
| 11 |
|
| 12 | // Sequelize |
| 13 | await sequelize.query(`SELECT * FROM t WHERE id = ${id}`); |
Explanation (EN)
Each call drops to raw SQL with the input concatenated in. $queryRawUnsafe, knex.raw with interpolation, TypeORM query(), and Sequelize query() without replacements all bypass the ORM's normal parameterization and are directly injectable.
Objašnjenje (HR)
Svaki poziv prelazi na raw SQL s ulazom ukomponiranim u string. $queryRawUnsafe, knex.raw s interpolacijom, TypeORM query() i Sequelize query() bez replacements zaobilaze uobičajenu parametrizaciju ORM-a i izravno su podložni injekciji.
Good example
| 1 | // Prisma — tagged template binds values automatically |
| 2 | const rows = await prisma.$queryRaw` |
| 3 | SELECT * FROM posts WHERE author = ${authorId} ORDER BY created DESC`; |
| 4 |
|
| 5 | // Knex — positional bindings |
| 6 | const users = await knex.raw('SELECT * FROM users WHERE name = ?', [name]); |
| 7 |
|
| 8 | // TypeORM — parameter array |
| 9 | await dataSource.query('DELETE FROM sessions WHERE token = $1', [token]); |
| 10 |
|
| 11 | // Sequelize — named replacements |
| 12 | await sequelize.query('SELECT * FROM t WHERE id = :id', { |
| 13 | replacements: { id }, |
| 14 | type: QueryTypes.SELECT, |
| 15 | }); |
Explanation (EN)
Prisma's $queryRaw tagged template, knex.raw with a bindings array, TypeORM's parameter array, and Sequelize's replacements all send values out-of-band so the database parses them as data. Use the ORM's typed builder when possible; when you need raw SQL, use these bound forms.
Objašnjenje (HR)
Prisma $queryRaw tagged template, knex.raw s poljem bindinga, TypeORM polje parametara i Sequelize replacements sve šalju vrijednosti izvan tijela upita pa ih baza parsira kao podatke. Koristi tipizirani builder ORM-a kad možeš; kad treba raw SQL, koristi ove vezane oblike.
Notes (EN)
A useful lint signal: ban the literal substring 'RawUnsafe' and template literals inside knex.raw/query() in code review.
Bilješke (HR)
Korisni lint signal: u code reviewu zabrani literal 'RawUnsafe' i template literale unutar knex.raw/query().
Exceptions / Tradeoffs (EN)
$queryRawUnsafe / knex.raw with interpolation are acceptable only for fully static SQL with no runtime values, or for dynamic identifiers validated against an allow-list (not bindable). Document why the unsafe variant is used.
Iznimke / Tradeoffi (HR)
$queryRawUnsafe / knex.raw s interpolacijom prihvatljivi su samo za potpuno statični SQL bez runtime vrijednosti, ili za dinamične identifikatore validirane prema allow-listi (koji se ne mogu vezati). Dokumentiraj zašto se koristi unsafe varijanta.