How to Prevent SQL Injection in Node.js
SQL injection is still one of the most exploited web vulnerabilities. Here's how it happens in Node.js — and exactly how to stop it with parameterized queries, ORMs, and validation.
TL;DR
Never concatenate user input into SQL strings. Use parameterized queries (placeholders like $1) or an ORM. Scan your repo free to find injection risks automatically.
What is SQL injection?
SQL injection happens when untrusted input is placed directly into a SQL query. An attacker crafts input that changes the query's meaning — bypassing authentication, dumping data, or deleting tables. It maps to OWASP A03: Injection.
The vulnerable pattern
This is the mistake — building the query with a template literal:
// ❌ VULNERABLE — user input concatenated into SQL
app.get('/users', async (req, res) => {
const { email } = req.query
const result = await db.query(
`SELECT * FROM users WHERE email = '${email}'`
)
res.json(result.rows)
})
// Attacker: /users?email=' OR '1'='1 → dumps every userFix #1: Parameterized queries
Pass values separately from the SQL. The driver (here pg) safely escapes them — the input can never change the query structure:
// ✅ SAFE — parameterized query ($1 placeholder)
app.get('/users', async (req, res) => {
const { email } = req.query
const result = await db.query(
'SELECT * FROM users WHERE email = $1',
[email]
)
res.json(result.rows)
})For mysql2, use ? placeholders with connection.execute(sql, [values]) — same principle.
Not sure if your code has injection risks?
Scan your repo for SQL injection — freeFix #2: Use an ORM
ORMs like Prisma, Sequelize, and TypeORM parameterize queries automatically:
// ✅ SAFE — ORM (Prisma) escapes inputs for you
const users = await prisma.user.findMany({
where: { email: req.query.email },
})⚠️ Caveat: raw query escape hatches (prisma.$queryRawUnsafe, Sequelize sequelize.query with interpolation) reintroduce the risk. Treat those like raw SQL.
Fix #3: Validate and constrain input
Defense in depth: validate types and formats at the edge (e.g. with zod), use least-privilege DB accounts, and never expose raw DB errors to users. Parameterization is the primary control — validation is the backup.
Catch it automatically
Manual review misses things. SwarmFlow's Security agent reads your code semantically, flags string-concatenated queries with the exact file:line, explains why it's exploitable, and generates a parameterized fix you can merge.
Scan your Node.js repo for injection flaws
Free, no credit card. Results in 30 seconds with paste-ready fixes.
Start Scanning Free