Problem: Bridge Callback and Promise APIs

Medium
30 min
Wrap a legacy callback-based client so it can be used with async/await.

Problem statement

Your system relies on a legacy database client that still uses Node.js-style callbacks:

db.query('SELECT * FROM users', (err, result) => { ... });

Modern code in your app, however, uses async/await everywhere. Mixing both styles leads to nested callback chains and inconsistent code. You’ve been asked to create an adapter that wraps the legacy client so that all queries can be awaited, without modifying the client itself.

Goal

Implement a DbClientAdapter class that wraps a callback-based client and exposes a promise-based method:

async query(sql)
  • Internally, it should call the original callback-style API.

  • Externally, it should return a promise that resolves or rejects based on the callback outcome.

Constraints

  • You cannot modify the legacy client.

  • You must convert the callback API to a promise-based interface using standard Node.js techniques.

  • Do not use external libraries (util.promisify is allowed but optional).

  • The adapter should behave exactly like a native promise-based client.

Sample output

The examples below illustrate what the output should look like:

(async () => {
const db = new DbClientAdapter(legacyDb);
try {
const users = await db.query('SELECT * FROM users');
console.log(users);
} catch (err) {
console.error('Error:', err.message);
}
/* Expected output:
[ 'Result of "SELECT * FROM users"' ] */
try {
await db.query('SELECT * FROM error');
} catch (err) {
console.error('Error:', err.message);
}
/* Expected output:
Error: Query failed */
})();

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Problem: Bridge Callback and Promise APIs

Medium
30 min
Wrap a legacy callback-based client so it can be used with async/await.

Problem statement

Your system relies on a legacy database client that still uses Node.js-style callbacks:

db.query('SELECT * FROM users', (err, result) => { ... });

Modern code in your app, however, uses async/await everywhere. Mixing both styles leads to nested callback chains and inconsistent code. You’ve been asked to create an adapter that wraps the legacy client so that all queries can be awaited, without modifying the client itself.

Goal

Implement a DbClientAdapter class that wraps a callback-based client and exposes a promise-based method:

async query(sql)
  • Internally, it should call the original callback-style API.

  • Externally, it should return a promise that resolves or rejects based on the callback outcome.

Constraints

  • You cannot modify the legacy client.

  • You must convert the callback API to a promise-based interface using standard Node.js techniques.

  • Do not use external libraries (util.promisify is allowed but optional).

  • The adapter should behave exactly like a native promise-based client.

Sample output

The examples below illustrate what the output should look like:

(async () => {
const db = new DbClientAdapter(legacyDb);
try {
const users = await db.query('SELECT * FROM users');
console.log(users);
} catch (err) {
console.error('Error:', err.message);
}
/* Expected output:
[ 'Result of "SELECT * FROM users"' ] */
try {
await db.query('SELECT * FROM error');
} catch (err) {
console.error('Error:', err.message);
}
/* Expected output:
Error: Query failed */
})();

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Node.js
// Simulated legacy callback-based DB client
const legacyDb = {
query(sql, callback) {
setTimeout(() => {
if (sql.includes('error')) {
callback(new Error('Query failed'));
} else {
callback(null, [`Result of "${sql}"`]);
}
}, 100);
}
};
// Your code here
// Example usage
(async () => {
const db = new DbClientAdapter(legacyDb);
try {
const users = await db.query('SELECT * FROM users');
console.log(users);
} catch (err) {
console.error('Error:', err.message);
}
/* Expected output:
[ 'Result of "SELECT * FROM users"' ] */
try {
await db.query('SELECT * FROM error');
} catch (err) {
console.error('Error:', err.message);
}
/* Expected output:
Error: Query failed */
})();