Open In App

How to Use Prepared Statements in MySQL with Node.js

Last Updated : 29 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

MySQL prepared a database by pre-compiling the SQL query with a set of placeholders for parameters. You could use the MySQL2 or MySQL library to be connected to your MySQL database and execute queries by passing the SQL statement with an array of values for the placeholders. It prevents SQL injection attacks by properly escaping the input from users and can often improve performance by allowing MySQL to reuse an execution plan for repeated queries with different parameters. Prepared statements become very useful in cases where either repeating or complex queries with dynamic data need to be executed.

Setting Up the Environment

Before looking at the prepared statements, let us set up the environment first:

Node.js: Download and install Node.js in your system from nodejs.org.

MySQL: Download and install MySQL. Then, create a database. You can download it from mysql.com.

Node.js MySQL Module: After this, you will need to install a Node.js module that gives you access to MySQL. You will need to run this line in your terminal:

npm install mysql

Connecting to MySQL from Node.js

You need to connect to the database to be able to use MySQL with Node.js. Here is how you can proceed:

JavaScript
onst mysql = require('mysql');

// Create a connection to database
const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'your_password',
  database: 'your_database'
});

// Connect to database
connection.connect((err) => {
  if (err) throw err;
  console.log('Connected to the MySQL database!');
});

Using Prepared Statement

Now that you are connected to the database, you can start working with prepared statements in order to execute SQL queries safely.

Preparing a Statement

A prepared statement is created with placeholders – normally by ?. You would bind values to those placeholders when executing the statement.

JavaScript
// preparaing statement
const sql = 'SELECT * FROM users WHERE id = ?';

Executing a Prepared Statement

You can then execute this prepared statement by passing the SQL query and an array of values to the query method:

JavaScript
//excuting the prepared statement
const userId = 1;
connection.query(sql, [userId], (err, results) => {
  if (err) throw err;
  console.log(results);
});

In the above example, the userId is safely bound to the placeholder ?, preventing SQL injection.

Inserting Data Using Prepared Statements

Prepared statements can also be quite efficient when it comes to inserting data into the database. Here's an example:

JavaScript
// insert data into prepared statement
const insertSql = 'INSERT INTO users (name, email) VALUES (?, ?)';
const user = ['John Doe', '[email protected]'];

connection.query(insertSql, user, (err, results) => {
  if (err) throw err;
  console.log('User added with ID:', results.insertId);
});

The example herein has the user array holding values that are to be inserted in the name and email fields respectively.

Updating Data Using Prepared Statements

Statements can also be prepared to update records in the database. This is done as shown below:

JavaScript
//update data
const updateSql = 'UPDATE users SET email = ? WHERE id = ?';
const data = ['[email protected]', 1];

connection.query(updateSql, data, (err, results) => {
  if (err) throw err;
  console.log('Rows affected:', results.affectedRows);
});

This example will change the email of the user with an id equal to 1.

Deleting Data Using Prepared Statements

The prepared statements also enable safe deletion of records. For example:

JavaScript
// delete data
const deleteSql = 'DELETE FROM users WHERE id = ?';
const userIdToDelete = 1;

connection.query(deleteSql, [userIdToDelete], (err, results) => {
  if (err) throw err;
  console.log('Rows deleted:', results.affectedRows);
});

This statement deletes the user with id = 1 from the database.

Closing the Connection

After the required database operations are performed, the database connection needs to be closed:

JavaScript
//close the connection 
connection.end((err) => {
  if (err) throw err;
  console.log('Database connection closed.');
});

Example of Parameterized Queries with Different Data Types

Parameterized queries are of great value for preventing SQL injection and guaranteeing database security. The next example show you how to use parameterized queries using different data types in Node.js, along with the mysql2 library.

Setup

Suppose that we have created a table, as follows, named employees in some database.

CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER,
salary REAL,
is_active BOOLEAN
);

Example of Parameterized Queries

Install the MySQL2 Library

First, you need to install the mysql2 library. Open your terminal and run:

npm install mysql2

Set Up the MySQL Connection

Next, set up a connection to your MySQL database using the mysql2 library. Create a file named db.js and add the following code:

JavaScript
const mysql = require('mysql2');

// Create a connection to the database
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'your-username',
    password: 'your-password',
    database: 'your-database-name'
});

// Connect to the database
connection.connect((err) => {
    if (err) {
        console.error('Error connecting to the database:', err.stack);
        return;
    }
    console.log('Connected to the database as id', connection.threadId);
});

module.exports = connection;

Replace your-username, your-password, and your-database-name with your actual MySQL credentials.

Using Parameterized Queries

Now, let's see some examples of how to use parameterized queries to prevent SQL injection attacks.

a. Select Query with Parameters

JavaScript
const connection = require('./db');

// Sample parameters
const userId = 1;

// Parameterized query using ?
const sql = 'SELECT * FROM users WHERE id = ?';
connection.execute(sql, [userId], (err, results) => {
    if (err) {
        console.error('Error executing query:', err.stack);
        return;
    }
    console.log('User details:', results);
});

In this example, ? is used as a placeholder for the userId parameter. The actual value is passed as an element of the array [userId] after the SQL string.

Insert Query with Parameters

JavaScript
const connection = require('./db');

// Sample data to insert
const name = 'John Doe';
const email = '[email protected]';

// Parameterized insert query
const sql = 'INSERT INTO users (name, email) VALUES (?, ?)';
connection.execute(sql, [name, email], (err, results) => {
    if (err) {
        console.error('Error inserting data:', err.stack);
        return;
    }
    console.log('Data inserted successfully. Insert ID:', results.insertId);
});

This example inserts a new user into the users table using parameterized values for name and email.

Update Query with Parameters

JavaScript
const connection = require('./db');

// Sample data to update
const userId = 1;
const newEmail = '[email protected]';

// Parameterized update query
const sql = 'UPDATE users SET email = ? WHERE id = ?';
connection.execute(sql, [newEmail, userId], (err, results) => {
    if (err) {
        console.error('Error updating data:', err.stack);
        return;
    }
    console.log('Data updated successfully. Affected Rows:', results.affectedRows);
});

This update query changes the email of a user with the specified userId.

Delete Query with Parameters

JavaScript
const connection = require('./db');

// Sample parameter
const userId = 1;

// Parameterized delete query
const sql = 'DELETE FROM users WHERE id = ?';
connection.execute(sql, [userId], (err, results) => {
    if (err) {
        console.error('Error deleting data:', err.stack);
        return;
    }
    console.log('Data deleted successfully. Affected Rows:', results.affectedRows);
});

This delete query removes a user from the users table based on the userId.

Closing the Connection

Remember to close the database connection when your application terminates to avoid potential memory leaks:

JavaScript
connection.end((err) => {
    if (err) {
        console.error('Error ending the connection:', err.stack);
        return;
    }
    console.log('Database connection closed.');
});

How to handle multiple queries using prepared statements within a single connection

Here’s how you can handle multiple queries using prepared statements within a single connection using the mysql2 library in Node.js.

Steps to Handle Multiple Queries Using Prepared Statements

Set Up Your Environment

Make sure Node.js is installed.

Install the mysql2 library using the command:

npm install mysql2

Create a Connection to MySQL

Use a single connection for all queries to manage resources efficiently. This connection can be reused across different operations.

JavaScript
const mysql = require('mysql2');

// Create a connection to the database
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'your-username',
    password: 'your-password',
    database: 'your-database'
});

// Connect to the database
connection.connect((err) => {
    if (err) {
        console.error('Error connecting to the database:', err.message);
        return;
    }
    console.log('Connected to the database.');
});

Replace 'your-username', 'your-password', and 'your-database' with your actual MySQL credentials.

Handling Multiple Queries Using Prepared Statements

You can use prepared statements with the connection.prepare() method. Here’s an example that inserts, updates, selects, and deletes records using prepared statements within a single connection.

JavaScript
// Prepare multiple queries as prepared statements

// 1. Insert multiple records
const insertStmt = connection.prepare(`
    INSERT INTO employees (name, age, salary, is_active)
    VALUES (?, ?, ?, ?)
`);

// 2. Update a record
const updateStmt = connection.prepare(`
    UPDATE employees SET salary = ? WHERE name = ?
`);

// 3. Select active employees
const selectStmt = connection.prepare(`
    SELECT * FROM employees WHERE is_active = ?
`);

// 4. Delete a record by ID
const deleteStmt = connection.prepare(`
    DELETE FROM employees WHERE id = ?
`);

// Execute the prepared statements in sequence
connection.beginTransaction((err) => {
    if (err) {
        console.error('Error starting transaction:', err.message);
        return;
    }

    // Insert records
    const employees = [
        ["Alice Johnson", 28, 72000.00, true],
        ["Bob Smith", 35, 68000.00, true],
        ["Carol White", 40, 85000.00, false]
    ];

    employees.forEach((employee) => {
        insertStmt.execute(employee, (err, results) => {
            if (err) {
                console.error('Error inserting data:', err.message);
                connection.rollback();
                return;
            }
            console.log('Inserted:', results.insertId);
        });
    });

    // Update salary
    updateStmt.execute([75000.00, "Alice Johnson"], (err, results) => {
        if (err) {
            console.error('Error updating data:', err.message);
            connection.rollback();
            return;
        }
        console.log('Updated Rows:', results.affectedRows);
    });

    // Select active employees
    selectStmt.execute([true], (err, rows) => {
        if (err) {
            console.error('Error fetching data:', err.message);
            connection.rollback();
            return;
        }
        console.log('Active Employees:', rows);
    });

    // Delete an employee
    deleteStmt.execute([2], (err, results) => {
        if (err) {
            console.error('Error deleting data:', err.message);
            connection.rollback();
            return;
        }
        console.log('Deleted Rows:', results.affectedRows);

        // Commit the transaction if everything was successful
        connection.commit((err) => {
            if (err) {
                console.error('Error committing transaction:', err.message);
                connection.rollback();
                return;
            }
            console.log('Transaction committed successfully.');
        });
    });
});

// Close the prepared statements
insertStmt.unprepare();
updateStmt.unprepare();
selectStmt.unprepare();
deleteStmt.unprepare();

Closing the Connection

After completing all operations, you should close the database connection to free up resources:

JavaScript
connection.end((err) => {
    if (err) {
        console.error('Error closing the database connection:', err.message);
        return;
    }
    console.log('Database connection closed.');
});

Explanation

  • Prepared Statements: The connection.prepare() is used for the creation of prepared statements for various operations. They are executed further with dynamic values passed as arrays.
  • Transactions: The connection.beginTransaction() initiates a transaction. If any of the queries fail, then connection.rollback() is called to undo all the changes made in that transaction. In the case where all queries succeed, then connection.commit() is called to save all changes.
  • Error Handling: Each statement checks for errors and rolls back the transaction in case of an error. This ensures that the database remains consistent in case something goes wrong with one of the operations.
  • Unprepare Statements: After the statements have been executed, the unprepare() call on each statement cleans up the prepared statement and frees resources.

Advantages of Prepared Statements

There are several advantages of using prepared statements in MySQL with Node.js:

  • Security: It avoids SQL injection by separating SQL logic and data.
  • Performance: It reduces the burden on parsing and compilation of the execution of SQL queries several times.
  • Reusability: It will efficiently execute the executed SQL query with parameters only once.

Conclusion

The use of prepared statements in MySQL with Node.js is an extremely essential practice in building secure and efficient applications. Prepared statements avoid SQL injection by separating SQL logic safely from user data, and improve performance by reducing parsing and compilation time for SQL queries over and over again. Besides securing your database, using prepared statements in a Node.js project will also clean your code, hence make it more maintainable and scalable. It ensures that all of your interactions with the database are secure and optimized for applications to be more solid and reliable.


Next Article
Article Tags :

Similar Reads