TutorialJune 4, 2026 · 7 min read

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 user

Fix #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 — free

Fix #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