🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

SQL Injection | SQL & Databases Tutorial

Learn about SQL Injection in this comprehensive SQL & Databases development tutorial. The deadliest vulnerability.

Total XP: 0|💻 sql XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

Select an unlocked node to view details root

🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

1Never concatenate inputs

If you build an insert string in Node like this: "INSERT INTO users (name) VALUES ('" + req.body.name + "')", you have created a SQL Injection vulnerability. If a user types '); DROP TABLE users; -- as their name, the database will execute it and delete your table. ALWAYS use Parameterized Queries (e.g., VALUES ($1)).

2Step-by-Step Breakdown

The Syntax. The INSERT INTO statement is used to add new rows to a table. You specify the table name, the columns you want to fill, and the values for those columns.

Single Row Insert. 'INSERT INTO users (name, email) VALUES ('Alice', 'a@b.com');'. Notice that strings require single quotes. Numbers do not.

Omitting Columns. If your table has an 'id' that auto-increments (SERIAL in Postgres), you DO NOT include it in your insert statement. The database calculates it automatically.

Default Values. If a column has a 'DEFAULT CURRENT_TIMESTAMP', you can omit it from the INSERT statement. The engine will automatically insert the current time.

Knowledge Check. If a table has an auto-incrementing id column and a created_at column with a default value of the current time, how many columns do you actually need to specify in your INSERT statement for a new user?

  • All of them, you must manually pass NULL for id
  • Only the required data columns (like name and email)

Multi-Row Inserts. You can insert 100 rows in a single command. Just separate the value groups with commas. 'VALUES ('A', 'a@b.com'), ('B', 'b@b.com');'. This is vastly faster than 100 separate inserts.

UPSERT (On Conflict). What if you try to insert an email that already exists? It crashes. Postgres has an 'UPSERT' feature: 'ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;'.

INSERT from SELECT. You can insert data by reading it from another table. 'INSERT INTO archive_users SELECT * FROM users WHERE status = 'banned';'.

Returning the ID. In Node.js, when you create a user, you usually need their new ID to send to the frontend. Use the RETURNING clause. 'INSERT INTO users (name) VALUES ('Bob') RETURNING id;'.

Summary. INSERT creates data. Bulk inserts are for speed, RETURNING is for Node.

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Confirm Before Submitting Forms That Trigger Bulk INSERT Operations

A form that inserts many rows at once (e.g. a bulk CSV import mapped to a multi-row INSERT) should show a clear, keyboard-accessible summary of how many rows will be created before submission, so screen reader and keyboard users aren't surprised by a large, hard-to-undo data change.

SEO Implications

  • 1

    Slow INSERT Operations Delay Server Responses on Submission Forms

    Inserting rows one at a time in a loop instead of using a single multi-row INSERT delays the server response a form waits on, which can hurt perceived performance metrics on pages that redirect or re-render after a successful submission.

Best Practices

Always Use Parameterized Queries for INSERT, Never String-Built SQL

Building an INSERT statement by concatenating request-body values directly into the SQL string (e.g. VALUES ('${req.body.name}')) lets an attacker inject arbitrary SQL. Use placeholders like VALUES ($1) with the driver's parameter binding instead.

Use Multi-Row INSERT for Bulk Data Instead of Looping One Row at a Time

Running INSERT INTO ... VALUES (...) inside a loop for each row means a separate network round-trip per row. Batching many rows into one INSERT with comma-separated VALUES groups is dramatically faster for bulk operations.

Frequent Bugs

THE BUG

A multi-row INSERT fails with a column count mismatch error on one row deep in a large batch.

THE FIX

Every VALUES group in a multi-row INSERT must supply the exact same number of values, in the same column order, as declared in the INSERT INTO (...) column list. A single malformed row fails the entire statement in most engines.

THE BUG

Code expecting the new row's auto-generated id back from an INSERT gets undefined in the application layer.

THE FIX

Most drivers don't return the inserted row by default. In Postgres, add a RETURNING id (or RETURNING *) clause to the INSERT statement so the new row's data comes back in the same round trip, instead of running a second SELECT.

Real-World Examples

Safely Inserting a New User with Parameterized Values

A signup endpoint needed to insert a new user record from untrusted form input without exposing the app to SQL injection, while immediately getting back the new user's id for the response.

-- Using parameterized placeholders, never string concatenation
const result = await client.query(
  'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id;',
  [req.body.name, req.body.email]
);
const newUserId = result.rows[0].id;

Interview Prep

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Building an INSERT statement with string-concatenated user input (SQL Injection)

// Wrong const query = `INSERT INTO users (name) VALUES ('${req.body.name}')`; // Correct const query = 'INSERT INTO users (name) VALUES ($1)'; client.query(query, [req.body.name]);

The Solution //

Concatenating request-body values directly into an INSERT statement lets an attacker close the string early and append arbitrary SQL, such as a DROP TABLE. Always use parameterized placeholders so the driver treats values strictly as data, never as part of the SQL text.

The Error //

Column/value count mismatch in a multi-row INSERT VALUES list

-- Wrong: second row is missing a value INSERT INTO users (name, email) VALUES ('Alice', 'a@b.com'), ('Bob'); -- Correct: every row supplies both values INSERT INTO users (name, email) VALUES ('Alice', 'a@b.com'), ('Bob', 'b@b.com');

The Solution //

Every VALUES group in a multi-row INSERT must supply the same number of values, in the same order, as the declared column list. A single row with a missing or extra value fails the entire statement in most engines.

Lesson Glossary

[01]Bulk Insert

Inserting many rows at once.

Code Preview
// Bulk Insert context

[02]UPSERT

Update or Insert.

Code Preview
// UPSERT context

Continue Learning