Safeguard
Open Source

Is the npm mysql Package Safe? A Security Review

A security-focused look at the npm mysql driver: SQL injection risks, prepared statements, connection handling, and why most teams should move to mysql2.

Karan Patel
Platform Engineer
6 min read

The npm mysql package is still functional and widely deployed, but it is effectively in maintenance-only mode, and the mysql2 package has become the recommended MySQL driver for new Node.js projects. If you already depend on npm mysql, the more urgent question is not the library itself but how your code uses it, because the majority of real incidents come from unsafe query construction rather than a flaw in the driver. This review covers the risks, the safe patterns, and when to migrate.

Where npm mysql stands today

The original mysql package remains one of the most downloaded MySQL clients for Node.js, pulling close to a million weekly downloads. But it has not shipped a new release to npm in over a year, which puts it in the category of a low-activity project. The community successor, mysql2, is far more actively maintained, releases regularly, and pulls well over ten million weekly downloads.

That difference matters for security. When a vulnerability is disclosed in a driver, an actively maintained package gets a patch quickly; a dormant one may not. Depending on a stalled package is not automatically dangerous, but it is a risk you should track deliberately rather than by accident.

The real risk is SQL injection, not the driver

Most security problems attributed to a MySQL client are actually application bugs. The classic mistake is building a query by concatenating user input:

// Dangerous: user input is spliced directly into SQL
const email = req.query.email;
connection.query(
  "SELECT * FROM users WHERE email = '" + email + "'",
  (err, rows) => { /* ... */ }
);

If email is ' OR '1'='1, the query returns every row in the table. Worse payloads can read other tables or, depending on privileges, modify data. This is SQL injection, and no driver protects you if you write queries this way.

The npm mysql package does offer escaping helpers, but relying on manual escaping is error-prone. The safer default is placeholders, which let the driver handle quoting:

connection.query(
  "SELECT * FROM users WHERE email = ?",
  [req.query.email],
  (err, rows) => { /* ... */ }
);

With placeholders, the input is always treated as a value, never as SQL syntax. Make this the only pattern your team allows.

Prepared statements: where mysql2 pulls ahead

The ? placeholder in npm mysql performs client-side escaping. It is safe when used correctly, but it is not a true server-side prepared statement. The mysql2 package supports real prepared statements through execute(), where the SQL template is sent to the server once and parameters are bound separately:

// mysql2 with a genuine prepared statement
const [rows] = await connection.execute(
  "SELECT * FROM users WHERE email = ?",
  [email]
);

Genuine prepared statements give you a stronger separation between code and data and can improve performance for repeated queries. For teams evaluating a migration, this is one of the concrete wins, alongside native promise support and better connection pooling.

Handle connections and secrets carefully

Query construction gets the most attention, but connection handling is a frequent source of trouble.

  • Do not hard-code credentials. Read the database password from an environment variable or secrets manager, never a committed config file. A leaked config.js in Git history is a real breach vector.
  • Use a connection pool rather than opening a raw connection per request. Unbounded connections under load can exhaust the database and become an availability problem.
  • Enable TLS for connections that cross a network boundary. Both npm mysql and mysql2 accept an ssl option; use it for any managed or remote database.
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  ssl: { rejectUnauthorized: true },
  connectionLimit: 10,
});

Keep transitive dependencies in view

Whichever driver you choose, it arrives with a small dependency tree of its own. A vulnerability in one of those transitive packages is still your exposure. Run npm's built-in audit on every install:

npm audit --production

Treat findings as work, not noise. For teams that want continuous visibility into transitive risk across many services, an SCA tool can track advisories against your lockfile and alert when a new CVE lands on a package you already ship. If you are weighing tooling options, our Snyk comparison covers what to look for in dependency scanning.

Should you migrate from npm mysql to mysql2?

For most teams, yes, but on your own schedule. The APIs are close enough that migration is usually mechanical: the query interface is largely compatible, and you gain promises, real prepared statements, and an actively maintained codebase. If you have a large, stable application with heavy use of npm mysql-specific behavior, test thoroughly before switching, but plan the move rather than staying on a dormant package indefinitely.

The migration itself is often as simple as changing the import and adopting execute() for parameterized queries:

// before
const mysql = require('mysql');
// after
const mysql = require('mysql2/promise');

FAQ

Is the npm mysql package deprecated?

It is not formally deprecated, but it has gone more than a year without a new release, which puts it in low-maintenance territory. The community treats mysql2 as the successor and recommended driver for new work.

Does npm mysql prevent SQL injection automatically?

No. It provides placeholder syntax and escaping helpers, but you have to use them. If you concatenate user input into query strings, you are vulnerable regardless of which driver you use. Always use ? placeholders with a parameter array.

What is the difference between escaping in mysql and prepared statements in mysql2?

The npm mysql package escapes values client-side before sending a fully-formed query. The mysql2 package's execute() uses server-side prepared statements, sending the SQL template and parameters separately. Both defend against injection when used correctly, but prepared statements offer a cleaner separation and can perform better on repeated queries.

How do I check whether my MySQL driver has known vulnerabilities?

Run npm audit after installing, and keep your lockfile committed so scans are reproducible. For ongoing monitoring across a fleet of services, a software composition analysis tool will re-check your dependencies against new advisories automatically.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.