Node.js
MySQL
Promises
Q library
Database Integration

Using Q with Node-Mysql

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The mysql package for Node.js uses callbacks, while Q is a promise library designed to wrap callback-style APIs. That combination still works in older codebases, even though it is now a maintenance pattern more than a modern best practice.

The Problem Q Is Solving

Plain node-mysql code is callback-based:

javascript
1const mysql = require("mysql");
2
3const connection = mysql.createConnection({
4  host: "localhost",
5  user: "root",
6  password: "secret",
7  database: "appdb",
8});
9
10connection.query("SELECT 1 AS value", function (err, results) {
11  if (err) {
12    console.error(err);
13    return;
14  }
15
16  console.log(results);
17});

This is fine for one query. It gets harder to manage when multiple queries depend on one another and you want centralized error handling.

Wrapping query With Q

Because query is a method on the connection object, Q.ninvoke is a good fit. It preserves the method binding while converting the call to a promise.

javascript
1const Q = require("q");
2const mysql = require("mysql");
3
4const connection = mysql.createConnection({
5  host: "localhost",
6  user: "root",
7  password: "secret",
8  database: "appdb",
9});
10
11function queryAsync(sql, values) {
12  return Q.ninvoke(connection, "query", sql, values);
13}
14
15connection.connect();
16
17queryAsync("SELECT ? + ? AS total", [2, 3])
18  .then(function (results) {
19    console.log(results[0].total);
20  })
21  .catch(function (err) {
22    console.error(err);
23  })
24  .finally(function () {
25    connection.end();
26  })
27  .done();

That gives you promise chaining without manually building a deferred for every query.

Why Method Binding Matters

One subtle problem with wrapping old callback APIs is losing the original method context. If you pass connection.query around like a normal function, it may no longer execute with connection as its owner.

That is why this pattern is useful:

javascript
Q.ninvoke(connection, "query", sql, values)

It tells Q to call the method on the object directly instead of treating it as a detached function.

Manual Deferreds Still Work

If you need more control, you can wrap the callback manually with Q.defer().

javascript
1const Q = require("q");
2
3function queryWithDeferred(connection, sql, values) {
4  const deferred = Q.defer();
5
6  connection.query(sql, values, function (err, results) {
7    if (err) {
8      deferred.reject(err);
9      return;
10    }
11
12    deferred.resolve(results);
13  });
14
15  return deferred.promise;
16}

This is valid, but it is more verbose than the helper methods and easier to get wrong if the wrapper logic becomes repetitive.

Chaining Multiple Queries

Promise wrapping becomes most useful when queries depend on earlier results.

javascript
1queryAsync("SELECT id FROM users WHERE email = ?", ["[email protected]"])
2  .then(function (rows) {
3    if (rows.length === 0) {
4      throw new Error("User not found");
5    }
6
7    return queryAsync("SELECT * FROM orders WHERE user_id = ?", [rows[0].id]);
8  })
9  .then(function (orders) {
10    console.log("Order count:", orders.length);
11  })
12  .catch(function (err) {
13    console.error("Database error:", err.message);
14  })
15  .done();

This is flatter and easier to read than nested callback blocks, which is the whole point of introducing Q into this style of codebase.

Legacy Caveat

For new projects, this is usually not the direction to choose. Native promises, async/await, and libraries such as mysql2/promise are more natural in modern Node.js.

Still, that does not make Q useless in an older system. If the codebase already uses Q extensively, wrapping node-mysql with Q can be a practical incremental improvement that avoids a full migration all at once.

Common Pitfalls

  • Wrapping connection.query without preserving the connection method context.
  • Mixing callbacks and Q promises inconsistently in the same control path.
  • Forgetting to close the connection or release pooled resources when the promise chain finishes.
  • Overusing Q.defer() where Q.ninvoke or another helper would be simpler.
  • Treating Q as a modern recommendation instead of a legacy maintenance technique.

Summary

  • 'node-mysql is callback-based, and Q can adapt it into promise-returning query helpers.'
  • 'Q.ninvoke(connection, "query", ...) is a good pattern because it preserves method binding.'
  • Promise chains make sequential database logic and centralized error handling easier to read.
  • Manual deferred wrappers are possible, but helper methods are usually cleaner.
  • For new code, native promises are usually better, but Q remains usable when maintaining older Node.js applications.

Course illustration
Course illustration

All Rights Reserved.