Node.js mysql error handling in promise - mysql

I'm using node.js and express, also mysql.
I use a connection pool to request connections and create a promise on it, to limit callback nightmare, the following snippet is set in a file that I import later, note that that I set an handler on error to not terminate the application in case of anything going really wrong
exports.getConnection = () => {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) {
reject(`Could not obtain the connection from the pool: ${err}`);
}
connection.on('error', err => {
console.log(`SQL error (code: ${err.code}, message: ${err.sqlMessage}) while executing query: ${err.sql}`);
});
resolve(connection);
});
});
};
And here is an example of usecase (the idea is to get the connection, chain the query in the then, and if a non fatal error happen I will throw it and handle the connection release in the catch handler
// Exception handler that release the connection then call the callback
function releaseConnectionHandler(err, connection, callback) {
connection.release();
callback(err, null);
}
exports.someRequest = function(ID, callback) {
sqlPool.getConnection().then(connection => {
connection.query("SELECT * from tableNotExists",
(err, result) => {
if (err) {
throw ({ err, connection, callback });
}
connection.release();
callback(null, result);
});
}).catch(({ err, connection, callback}) => releaseConnectionHandler(err, connection, callback));
};
The query will fail, but I see that the handler is not even called (I put some trace in it...) and the application terminates on
node_modules/mysql/lib/protocol/Parser.js:80
throw err; // Rethrow non-MySQL errors
Correct querie yoeld no troubles...Any ideas what I did wrong on the error handling ?

You're re-throwing the error passed to the callback of your query, which the library you're using then re-throws as well, and finally isn't properly caught and handled anywhere and results in a failure. You're not in the context of the Promise when you throw, but the context of the callback function called from the mysql module.
You're also unnecessarily mixing promises and callbacks, in particular the function you're exporting. Your question indicates that you want to move away from callbacks, so I'm going to base this answer on that indication.
To solve the main issue, don't throw the error. Instead, pass it up to the callee:
const promisify = require("util").promisify;
exports.someRequest = function (ID) {
return sqlPool.getConnection().then(connection => {
return promisify(connection.query)("select * from tableNotExist")
.finally(connection.release);
});
};
The connection will always be released back to the pool, whether successful or on error. You can then call the method with:
yourModule.someRequest(id).then((results) => {
// Do something with the result set
}).catch((e) => {
// Handle error. Can be either a pool connection error or a query error.
});
If you have the possibility to use async/await, the code can be rewritten:
const promisify = require("util").promisify;
exports.someRequest = async function (ID) {
let connection = await sqlPool.getConnection();
try {
return await promisify(connection.query)("select * from tableNotExist");
} finally {
connection.release();
}
};
I also recommend using node-mysql2 since they have a Promise-based API in addition to their callback-style API and in my experience better performance as well. Then you don't have to write these tedious wrappers and instead just require('mysql2/promise') and be good to go.

Related

How can i stop further execution of express middleware

I want to get this functionality if(thereIsSomeError) //stop executing further. for example if there some error accurs in middleware or in the callback then i don't want to execute callback(in the app.route) and the middleware further
I tried this code. But i'm still getting req.err as true. how can i fix this issue
// My MiddleWare
export let Middleware=()=> {
return (req,res,next)=>{
next()
console.log(req.err) // Problem is here.. i'm still getting req.err(true)
if(!req.err){
db.query(`query`,(error, responseData)=>{
if(error) console.log(error)
db.query(`second query`,{...// send data to the
database})
})
}
}
}
//End point
app.post('/addStudent',Middleware, (req, res) => {
//setting error to true initially
req.err=true;
let data = req.body
db.query(`query `, data.username, (err, d) => {
if (err) return res.json(err)
else {
// since no Error accured so set the error to false
req.err=false;
let q = 'query';
let values = {//data here}
db.query(q, values, (err, data) => {
if (err) return res.status(200).json(err)
else return res.status(200).json({ data })
})
}
})
})
First, a middleware runs BEFORE a request, NOT AFTER. If you set req.err = true in your POST endpoint, IT WILL STAY TRUE, meaning your database call will certainly return an error.
Second, to successfully abort a middleware call, use return. Returning a function stops it immediately. You can choose either to return next(err) to forward the error to the handler, or to use return res.send('Error') to terminate the response in the middleware.

NodeJS mysql app freezing without error - how to diagnose

My Node app is freezing when running a function I've written. The code will stop executing at a specific line on the 11th loop, with no error message. No further requests can be made to the app, and it must be restarted. The data is changing every time it's run, and it is always the 11th iteration.
The function it is calling is relatively complex, so I can't put the code here. My question is how do you approach diagnosing a piece of code when you see no failure notices? I assume the problem is mysql-related, as the last thing logged in the simplified version of the code below is 'Org request start 11' - no error message ever follows. But I don't understand how the code can stop executing here without entering the callback.
Example code is here:
async function processDataset(datasets){
for (let i = 0; i < datasets.length; i++) {
const dataset= datasets[i];
const orgData = await getOrgData(dataset.id);
const parsedDataset = await processDataset(dataset, orgData);
}
}
function getOrgData(datasetId) {
return new Promise(function (resolve, reject) {
console.log("Org request start", datasetId);
connection.getConnection(function (err, connection) {
console.log("got connection");
if (err) {
console.log(err);
reject(err);
}
connection.query("select * from orgs;", function (error, rows, fields) {
console.log("query returned");
connection.release();
if (error) {
console.log(error);
reject(error);
}
resolve(rows);
});
});
});
}
Running Node v12 / Express, mysql 5.5
So I found the problem was that elsewhere I had a query running that was missing a line to release the mysql connection in the second function called. Adding this resolved the bug. I don't understand why this doesn't trigger an error, or how I would have gone about diagnosing this with a more systematic approach, so would like to hear any comments.

Express.js using await with passport

I'm trying to add mySQL to passport.js to authenticate users in express.js, but can't seem to get await working.
Server.js:
initializePassport(
passport,
function(email) {
pool.getConnection(function(err, connection) {
if (err) throw err;
console.log("Connected!");
pool.query("SELECT * FROM users WHERE email = ?", email, function (err, result) {
if (err) throw err;
return result[0];
connection.release();
});
});
},
)
The passport config
function initialize(passport, getUserByEmail) {
const authenticateUser = async (email, password, done) => {
try {
const user = await getUserByEmail(email);
console.log(user)
} catch (e) {
return done(e)
}
Right now it just prints undefined for user, and then prints Connected. I'm not sure why the await user isn't working.
Well if that's getUserByEmail(), then it doesn't return a promise that is connected to when it's asynchronous operations are done, therefore, doing await getUserByEmail() doesn't wait for anything.
await ONLY does something useful if you are awaiting a promise that is connected to the operation you want to await for. Since you aren't even awaiting a promise, that await does nothing useful. You would need to change getUserByEmail() so that it returns a promise that is connected to the asynchronous operation you're trying to wait for.
For a function to return a promise that is connected to the asynchronous operations, you need to use promise-based asynchronous operations, not plain callback asynchronous operations, everywhere in that function. These are all database operations and all modern databases have a promise-based interface now so what you really want to do is to switch .getConnection(), .query() and .release() to all use promise-based operations. This will also make it a lot simpler to implement proper error handling and proper communication back to the caller of errors.
I don't know mysql particularly well myself, but here's a general idea. The promise interface comes from the module mysql2/promise:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({...});
initializePassport(passport, async function(email) {
let connection;
try {
connection = await pool.getConnection();
console.log("Connected!");
let result = await pool.query("SELECT * FROM users WHERE email = ?", email);
return result[0];
} catch(e) {
// log the error and the rethrow so the caller gets it
console.log(e);
throw e;
} finally {
if (connection) {
connection.release();
}
}
});

javascript promise catch confusion [duplicate]

What is the best way to handle this scenario. I am in a controlled environment and I don't want to crash.
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
When throwing from within the setTimeout we will always get:
$ node bluebird.js
c:\blp\rplus\bbcode\scratchboard\bluebird.js:6
throw new Error("AJAJAJA");
^
Error: AJAJAJA
at null._onTimeout (c:\blp\rplus\bbcode\scratchboard\bluebird.js:6:23)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
If the throw occurs before the setTimeout then bluebirds catch will pick it up:
var Promise = require('bluebird');
function getPromise(){
return new Promise(function(done, reject){
throw new Error("Oh no!");
setTimeout(function(){
console.log("hihihihi")
}, 500);
});
}
var p = getPromise();
p.then(function(){
console.log("Yay");
}).error(function(e){
console.log("Rejected",e);
}).catch(Error, function(e){
console.log("Error",e);
}).catch(function(e){
console.log("Unknown", e);
});
Results in:
$ node bluebird.js
Error [Error: Oh no!]
Which is great - but how would one handle a rogue async callback of this nature in node or the browser.
Promises are not domains, they will not catch exceptions from asynchronous callbacks. You just can't do that.
Promises do however catch exceptions that are thrown from within a then / catch / Promise constructor callback. So use
function getPromise(){
return new Promise(function(done, reject){
setTimeout(done, 500);
}).then(function() {
console.log("hihihihi");
throw new Error("Oh no!");
});
}
(or just Promise.delay) to get the desired behaviour. Never throw in custom (non-promise) async callbacks, always reject the surrounding promise. Use try-catch if it really needs to be.
After dealing with the same scenario and needs you are describing, i've discovered zone.js , an amazing javascript library , used in multiple frameworks (Angular is one of them), that allows us to handle those scenarios in a very elegant way.
A Zone is an execution context that persists across async tasks. You can think of it as thread-local storage for JavaScript VMs
Using your example code :
import 'zone.js'
function getPromise(){
return new Promise(function(done, reject){
setTimeout(function(){
throw new Error("AJAJAJA");
}, 500);
});
}
Zone.current
.fork({
name: 'your-zone-name',
onHandleError: function(parent, current, target, error) {
// handle the error
console.log(error.message) // --> 'AJAJAJA'
// and return false to prevent it to be re-thrown
return false
}
})
.runGuarded(async () => {
await getPromise()
})
Thank #Bergi. Now i know promise does not catch error in async callback. Here is my 3 examples i have tested.
Note: After call reject, function will continue running.
Example 1: reject, then throw error in promise constructor callback
Example 2: reject, then throw error in setTimeout async callback
Example 3: reject, then return in setTimeout async callback to avoid crashing
// Caught
// only error 1 is sent
// error 2 is reached but not send reject again
new Promise((resolve, reject) => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Nothing happen
})
.then(() => {})
.catch(err => {
console.log("Error", err);
});
// Uncaught
// error due to throw new Error() in setTimeout async callback
// solution: return after reject
new Promise((resolve, reject) => {
setTimeout(() => {
reject("error 1"); // Send reject
console.log("Continue"); // Print
throw new Error("error 2"); // Did run and cause Uncaught error
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});
// Caught
// Only error 1 is sent
// error 2 cannot be reached but can cause potential uncaught error if err = null
new Promise((resolve, reject) => {
setTimeout(() => {
const err = "error 1";
if (err) {
reject(err); // Send reject
console.log("Continue"); // Did print
return;
}
throw new Error("error 2"); // Potential Uncaught error if err = null
}, 0);
})
.then(data => {})
.catch(err => {
console.log("Error", err);
});

How to structure Node.js script with MySQL so that connection doesn't close prematurely

So I am making my first attempt with Node and I can't really wrap my head around how to work with the MySQL connection. The script is somewhat simplified like this
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '192.168.40.1',
user : 'user',
password : 'password',
database : 'database'
});
function DoSomething(connection, item, callback) {
connection.query(
'SELECT COUNT(*) AS count FROM another_table WHERE field=?',
item.field,
function (err, results) {
if (err) throw err;
if (results.length > 0 && results[0].count >= 1) {
callback(err, connection, item, 'Found something')
}
});
}
function DoSomethingElse(connection, item, callback) {
// Similar to DoSomething()
}
function StoreResult(err, connection, item, reason) {
if (err) throw err;
connection.query(
'INSERT INTO result (item_id, reason) VALUES (?, ?)',
[item.id, reason],
function (err, results) {
if (err) throw err;
});
}
connection.query('SELECT * FROM table WHERE deleted=?', [0], function (err, results)
{
if (err) throw err;
results.forEach(function (item, index) {
DoSomething(connection, item, StoreResult);
DoSomethingElse(connection, item, StoreResult);
});
});
connection.end();
What I am having trouble with (as far as I can tell) is that since DoSomething() it seems that connection.end() is called before all of the DoSomething()'s have finished causing errors that queries can't be performed when the connection is closed.
I tried playing around with the async library, but I haven't gotten anywhere so far. Anyone with some good advice on how to do this?
The problem with your code is that you're closing the connection synchronously while an asynchronous request is still being handled. You should call connection.end() only after all query callbacks have been called.
Since you are doing multiple queries, this means using some way to wait for all their results. The simplest way is to nest every next call into the callback of the previous one, but that way leads to the pyramid of doom. There are a number of popular libraries that solve this, but my own preference is for async.
Using async I would rewrite your code as follows:
async.waterfall([function(next) {
connection.query('SELECT * FROM table WHERE deleted=?', [0], next); // note the callback
},
function(results, next) {
// asynchronously handle each results. If they should be in order, use forEachSeries
async.forEach(results, function(item, next) {
// handle in parallel
async.parallel([function(done) {
DoSomething(connection, item, function(err, connection, item, reason) {
// This is a hack, StoreResult should have a callback which is called
// after it's done, because the callback is now being called too soon
StoreResult(err, connection, item, reason);
callback(err);
});
}, function(done) {
DoSomethingElse(connection, item, function(err, connection, item, reason) {
// This is a hack, StoreResult should have a callback which is called
// after it's done, because the callback is now being called too soon
StoreResult(err, connection, item, reason);
callback(err);
}], function(err) {
// this callback is called with an error as soon as it occurs
// or after all callbacks are called without error
next(err);
});
}, function(err) {
// idem
next(err);
});
}], function(err, results) {
// idem
// Do whatever you want to do with the final error here
connection.end();
});
This also allows you to solve a possible issue with the order of your queries in the forEach: They are started in order, but are not guaranteed to finish in order due to their asynchronous nature.
Close your connection after you have done everything you want in the script.
When programming in asynchronous language, keep in mind that the real ending point of your script is the last asynchronous callback, instead of the last line like other scripts (e.g. PHP).
Note that you don't want to simply ignore the connection.end(); as the underlying MySQL driver will keep the connection alive and your script will stuck in the last line forever until you kill it.
This is the modified version of your code.
connection.query('SELECT * FROM table WHERE deleted=?', [0], function (err, results)
{
if (err) throw err;
results.forEach(function (item, index) {
DoSomething(connection, item, StoreResult);
DoSomethingElse(connection, item, StoreResult);
});
// End your connection here
connection.end();
});