Recover from Uncaught Exception in Node.JS - exception

OK, so I have a problem. If an uncaught exception occurs while I am handling an HTTP request, I have no opportunity to call the end() method on the http.ServerResponse object. Therefore, the server hangs forever and never fulfills the request.
Here's an example:
var express = require('express');
var app = express.createServer();
var reqNum = 0;
app.get('/favicon.ico', function(req, res) {res.send(404);});
app.get('*', function(req, res, next) {
console.log("Request #", ++reqNum, ":", req.url);
next();
});
app.get('/error', function(req, res, next) {
throw new Error("Problem occurred");
});
app.get('/hang', function(req, res, next) {
console.log("In /hang route");
setTimeout(function() {
console.log("In /hang callback");
if(reqNum >= 3)
throw new Error("Problem occurred");
res.send("It worked!");
}, 2000);
});
process.on('uncaughtException', function(err) {
console.log("Uncaught exception!", err);
});
app.listen(8080);
If you visit /error, an exception occurs, but it is caught. The user receives an error message - no problem. If I visit /hang, though, the server will eventually throw an uncaught exception and hang forever. Any subsequent requests for /hang will hang.
This sucks. Any advice for how to fix this issue?

When an uncaught exception occurs, you're in an unclean state. Let the process die and restart it, there's nothing else you can do to safely bring it back to a known-good state. Use forever, it'll restart your process as soon as it dies.

If error is thrown synchronously, express won't stop working, only returning 500.
this.app.get("/error", (request, response) => {
throw new Error("shouldn't stop");
});
If error is thrown asynchronously, express will crash. But according to it's official documentation, there is still a way to recover from it by calling next:
this.app.get("/error", (request, response, next) => {
setTimeout(() => {
try {
throw new Error("shouldn't stop");
} catch (err) {
next(err);
}
}, 0);
});
This will let express do its duty to response with a 500 error.

Use try/catch/finally.
app.get('/hang', function(req, res, next) {
console.log("In /hang route");
setTimeout(function() {
console.log("In /hang callback");
try {
if(reqNum >= 3)
throw new Error("Problem occurred");
} catch (err) {
console.log("There was an error", err);
} finally {
res.send("It worked!");
}
}, 2000);
});

Related

Node MySQL hanging

I have my GET endpoint which is just a simple SELECT * FROM tableName and returns all the rows in the table but it keeps hanging. When I hit it in POSTMAN locally it just keep spinning with
Sending request... and the program never errors.
The get endpoint is hanging after connection.query(...) and the fourth console.log("HIT) is not printed
router.get('/allRows', async (req, res) => {
console.log("HIT")
if (req.query.tableName) {
console.log("HIT")
connection.connect((err) => {
if (err) console.log(err);
console.log("HIT")
connection.query(`SELECT * FROM ${req.query.tableName};`), (err, result, fields) => {
console.log("HIT")
if (err) console.log(err);
console.log(result)
res.status(200).json({
message: `Successfully got all data from ${req.query.tableName}`,
result: result
});
};
connection.end((err) => {
if (err)
console.error("Error when closing connection", err)
});
})
}
else {
res.status(400).json({
message: `Please provide table name`,
});
console.log('Missing table name');
}
});
I have 3 "HIT" logs at the moment, any ideas?
I noticed that if I cancel the endless request in postman and I hit the endpoint again I get a PROTOCOL_ENQUEUE_AFTER_QUIT error
EDIT
I changed my code to this:
router.get('/allRows', async (req, res) => {
console.log("HIT")
// also another guard for table name inside an array is needed
if (req.query.tableName) {
console.log("HIT")
connection.connect();
connection.query(`SELECT * FROM ${req.query.tableName}`), (err, rows, fields) => {
console.log("HIT", rows)
if (err) console.log(err);
res.status(200).json({
message: `Successfully got all data from ${req.query.tableName} LIMIT 1`,
result: rows
});
console.log("result")
};
}
else {
res.status(400).json({
message: `Please provide table name`,
});
console.log('Missing table name');
}
});
Still get the same issue and only 2 console hits... I have ran the debugger and when it gets to connection.query it just skips over it and doesn't go into the arrow function.
debugger at connection.query
debugger after connection.query
According to module docs, connection.connect is going to build live linking to database.
Just call it at server startup, and stop it when sever down.
Call connection.query when you want to solve requests.
connection.connect()
router.get(
.....
connection.query()
)
server.on(‘close’, ()=> connection.close()) // I guess
with connect error:
connection.connect(function(err) {
console.log(err.code); // 'ECONNREFUSED'
console.log(err.fatal); // true
});
router.get(
.....
connection.query()
)
server.on(‘close’, ()=> connection.close()) // I guess

NodeJS Waterfall respond with different error than waterfall callback

I am wondering what the proper way is to make a server response in a NodeJS Express app when an internal server error occurs. Here is some simplified code for registering users. The main issue being, if an internal server error happens, I want to log the error, but also respond to the client with a different message. What I have below is what my current solution is, but I feel like I'm not doing it properly (or not following the conventional way). I currently have an async waterfall setup which is called from the route.
//Controller.js
function verifyInputs(user, resCallback, callback) {
//verify user inputs (ie. passwords match)
if (valid) {
callback(null)
} else {
resCallback('whatever was wrong with inputs', 409)
callback('ok')
}
}
function checkIfUserExists(user, resCallback, callback) {
db.getPool().getConnection((err, connection) => {
if (err) {
resCallback('custom response error message', 500)
callback(err)
return
}
var sql = 'SELECT...'
connection.query(sql, (err, results) => {
connection.release()
if (err) {
resCallback('another custom response error', 500)
callback(err)
return
}
if (results.length > 0) {
resCallback('user already exists')
callback('ok')
}
})
)
}
module.exports.registerNewUser(user, callback) {
async.waterfall([
async.apply(user, callback, verifyInputs),
async.apply(user, callback, checkIfUserExists)
],
function(err, reults) {
if (err === 'ok') return
//log error or whatever here
})
}
This register function is called from the routes function:
//Router.js
router.post('/register', (req, res, next) => {
var newUser = //get user data from req
controller.registerNewUser(newUser, (msg, statusCode) => {
res.statusCode(statusCode)
res.send(msg)
})
})
The code above shows how I log the error while responding to the client with a different message. Is this the right or an OK way to do this?
Or maybe I shouldn't use a waterfall at all for this, and do something like this which would give me access to the res object at all stages without multiple callbacks:
router.post('/register', verifyInputs(), checkIfUserExists(), (req, res, next) => {
var newUser = //get user data from req
controller.registerNewUser(newUser, (msg, statusCode) => {
res.statusCode(statusCode)
res.send(msg)
})
})
I'm relatively new to server back end programming, and I am new to NodeJS and Express. I just want to make sure what I am doing the proper.

mysql connection rest service

Trying to make a simple rest service. The rest service is for pulling up a table from a local database. This rest service a want to make available for an android app.
Having trouble getting passed .then block. Tried catching the error but with no success. How do you catch the error if it's going wrong in the first .then
The below piece of code is the db.js, and sets up the connection to the database.
var sqlDb = require("mysql");
var settings = require("../settings");
exports.executeSql = function (sql, callback) {
var conn = new sqlDb.createConnection(settings.dbConfig);
conn.connect()
// !! Error unhandled
.then(function () {
var req = new sqlDb.Request(conn);
req.query(sql)
.then(function (recordset) {
callback(recordset);
})
.catch(function (err) {
console.log(err);
callback(null, err);
});
})
.catch(function (err) {
console.log(err);
callback(null, err);
});
};
After setting up connection the below piece of code is executed. With error handling.
var db = require("../core/db");
exports.getList = function (req, resp) {
db.executeSql("SELECT * FROM employees", function (data, err) {
if (err) {
// throws back error to web
resp.writeHead(500, "Internal Error", { "Content-Type":
"application/json" });
resp.write(JSON.stringify({ data: "ERROR occurred:" + err }));
} else {
resp.writeHead(200, { "Content-Type": "application/json" });
resp.write(JSON.stringify(data));
}
resp.end();
});
};
Made a separated js file for settings such as database. Tested my connection to the db on a same way. Excluded that problem but it keeps returning an error unhandled on the first .then. I'm not familiar with methods till now.
I think I found the problem. new sqlDb.Request(conn); The .Request is not available when using mysql. But how can I fix this
If you catch() an error it will not be caught again without returning a new rejection. Like this:
conn.connect()
.then(function () {
var req = new sqlDb.Request(conn);
// note the "return" here
return req.query(sql)
.then(function (recordset) {
callback(recordset);
})
.catch(function (err) {
console.log(err);
callback(null, err);
// note the line below
return Promise.reject(err)
});
})
.catch(function (err) {
console.log(err);
callback(null, err);
});
};
PS. Do you really need the callback(), why not use the Promise?

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);
});

Node js : Entry into the error block only

This is my app.js generated by express js while installing:
// catch 404 and forward to error handler
app.use(function(req, res, next) {
console.log("coming to this use func");
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
console.log("Dev request sent");
console.log(err.message);
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
console.log("testing this function");
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
// I wrote this code to detect '127.0.0.1:3000/enter' in the url
// correspondingly fetches questions from the database
app.post('/enter', function(req, res, next) {
console.log("Coming here.");
connection.query('SELECT * from questions_table', function(err, rows, fields) {
if (!err) {
console.log('The solution is: ', rows);
var myData = JSON.stringify(rows);
res.render('display', {myData:rows })
}
else {
console.log('Error while performing Query.');
res.render('display', { title: 'The index page!'})
}
});
});
The control never comes to the chunk that I wrote, rather it gets into the if loop that says :
if (app.get('env') === 'development')
ALWAYS and throws some error. Front end is a mere form submission. What am I doing wrong here?
Thanks in advance.