I want to throw an error exception for an redis.set callback and catch in try-catch block and then get control to error handling express middleware.
try {
redis.get('key', (err, reply) => {
if(err) throw err;
if(!reply) throw new Error('Can't find key');
});
}
catch{
next(error);
}
the problem is, that try-catch is simply not working, error is going to node console, but server is responding with 200 status.
You cant catch async events. Use promises for that:
const getKey = new Promise((res,rej) => {
redis.get('key', (err, reply) => {
if(err) return rej(err);
res(reply);
});
});
So one can do:
getKey.catch(next);
getKey.then(reply => {
//do whatever
next();
});
Related
So I have this piece of code that works fine, but if there is an error with the data type the try catch blocks are not working. It appears I am going to have to check all data types before using them to ensure my server doesn't crash.
For instance in the following code, If tokens for some reason wasn't an array, when the error get's thrown, my try catch isn't catching the error. Instead it is crashing the server.
router.patch("/updateDeviceToken", checkIfAuthenticated, (req, res) => {
try {
console.log("In updating contacts");
console.log("body", req.body);
User.findById(req.authId, (error, user) => {
try {
if (error) {
console.log(error);
console.log("user found");
return res.status(404).json(error);
}
if (!user) return res.status(401).json("User not found");
if (
req.body.android &&
!user.tokens.android.includes(req.body.android)
) {
user.tokens.android.push(req.body.android);
user
.save()
.then((user) => {
console.log("token updated");
return res.json("Token updated");
})
.catch((error) => res.status(404).json(error));
} else if (req.body.iOS && !user.tokens.iOS.includes(req.body.iOS)) {
user.tokens.iOS.push(req.body.iOS);
user
.save()
.then((user) => {
console.log("token updated");
return res.json("Token updated");
})
.catch((error) => res.status(404).json(error));
} else {
console.log("token received but not updated");
return res.json("Token received but not updated");
}
} catch (e) {
console.log(e);
return res.status(404).json("There was an error");
}
});
} catch (e) {
console.log(e);
}
});
try catch doesnt work well with then
Can you change code to use promises with await / async ?
You can use try catch with it
I struggled with that as it is easily overlooked in the documentation but For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them. For example:
app.get('/', function (req, res, next) {
fs.readFile('/file-does-not-exist', function (err, data) {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
You can also write your custom error handler, long read but it will save you a lot of time.
Problem
My try doesn't catch error if it is inside of MongoClients connect function
Environment
Linux (Mint, Tessa)
Node.js v10.16.0 (using ES6 with nodemon)
MongoClient (from mongodb npm repository)
Example
If I try this:
try {
throw new Error('This is error');
} catch(e) {
console.log(`Catched: ${e}`);
}
I get clean exit (it's fine - working)
Catched: Error: This is error
[nodemon] clean exit - waiting for changes before restart
But this doesn't work
If I try it in MongoDBs connect function:
try {
MongoClient.connect(config.url, config.options, (err, db) => {
if (err) { throw new Error('This is error'); }
});
} catch (err) {
console.log(`Catched: ${e}`);
}
I get app crashed
Error: This is error
[nodemon] app crashed - waiting for file changes before starting...
So it means it didn't catch my exception.
Try this
try {
let db = await MongoClient.connect(config.url, config.options);
} catch (err) {
console.log(`Catched: ${err}`);
}
Try to write code in async-await/sequential style if you want try catch to work.
Here you can see that you're getting err as first argument in callback, why would it go to catch block ? Same thing happens with func1().then().catch() style code.
Note: use async keyword in front of your function name if you want to use await.
eg:
async function test() {
try {
let db = await MongoClient.connect(config.url, config.options);
} catch (err) {
console.log(`Catched: ${err}`);
}
}
MongoClient.connect(config.url, config.options, (err, db) => {
if (err) { throw new Error('This is error'); }
});
Try to handle every exception in my async code (nodeJS, ExpressJS):
Here is almost pseudo code. I use limiter (npm limiter) module with method removeTokens (num, callback(err,remainingRequest)). Big part of code is inside the callback, and I wanna catch and throw any error there to the handler, but for now the error inside callback is still marked as "unhandled exception" and I don't understand why.
app.post('/', async (req, res) => {
try {
...
return getAll();
async function getAll () {
limiter.removeTokens(1, async (err, remainingRequest) => {
try {
throw new Error('THROWN')
} catch (error) {
throw error
}
})
}
} catch (error) {
console.log(error);
}
});
You shouldn't pass async functions into things that don't expect them (unless you catch all errors, as you are with your app.post callback). Instead, give yourself a wrapper for limiter.removeTokens that returns a promise:
function removeTokens(limiter, id) {
return new Promise((resolve, reject) => {
limiter.removeTokens(id, (err, remainingRequest) => {
if (err) {
reject(err);
} else {
resolve(remainingRequest);
}
});
});
}
(You might also look into util.promisify for that.)
Then:
app.post('/', async (req, res) => {
try {
...
await getAll(); // *** Or you might just use `removeTokens(limiter, 1)` directly here
function getAll() {
return removeTokens(limiter, 1);
}
} catch (error) {
console.log(error);
}
});
Here it is using removeTokens directly:
app.post('/', async (req, res) => {
try {
...
await removeTokens(limiter, 1);
} catch (error) {
console.log(error);
}
});
Firstly if possible please share as much code as you can as then it is easy for us to debug where the problem might be.
Coming you your question i think the problem is that in your try..catch block you are throwing the error instead of handling it with a reject. Below i have pasted a code block which you can try and let me know if it works for you. Please not the syntax might be different but the idea is that you have to reject the Promise in case of error.
`````````limiter.removeTokens(1, async (err, remainingRequest) => {
````````````try {
```````````````throw new Error('THROWN')
````````````} catch (error) {
```````````````return Promise.reject(error) //
````````````}
`````````})
``````}
```} catch (error) {
``````console.log(error);
```}
})
does it make sense to call the same http request call within the catch if the first one fails but with different parameter in order to return some default data ?
var defaultData = false;
clientService.getClients(defaultData)
.then(function (res) {
//do something
}).catch(function (err) {
defaultData = true;
clientService.getClients(defaultData)
.then(function (res) {
//do something
}).catch(function (err) {
console.log(err)
});
});
or this is a bad way ?
Be sure to return the new promise to the catch handler. The code will then chain properly and it avoids nesting:
clientService.getClients({defaultData:false})
.catch(function (err) {
return clientService.getClients({defaultData: true})
}).then(function (res) {
//return something
}).catch(function (err) {
console.log(err)
//IMPORTANT re-throw err
throw err;
});
This issue not bad but I think you have to find reason of the failure and on the furniture do some action.
the best is that you have to create an error handler like:
errorHandler(error:any){
///.....
}
In this method you should check status code of response, for instance if it is 500, you can't call again. or some thing like this.
I'm using the bluebird Promise library. I'd like to chain promises and catch specific promises errors. Here's what I'm doing :
getSession(sessionId)
.catch(function (err) {
next(new Error('session not found'));
})
.then(function (session) {
return getUser(session.user_id);
})
.catch(function (err) {
next(new Error('user not found'));
})
.then(function (user) {
req.user = user;
next();
});
But if an error is thrown by getSession, the two catch are called, as well as the second then. I'd like to stop the error propagation at the first catch, so that the second catch is only called when getUser throws, and the second then when getUser succeeds. What do?
The promise that is returned by the .catch method will still be resolved with the result of the callback, it doesn't just stop the propagation of the chain. You will either need to branch the chain:
var session = getSession(sessionId);
session.catch(function (err) { next(new Error('session not found')); });
var user = session.get("user_id").then(getUser);
user.catch(function (err) { next(new Error('user not found')); })
user.then(function (user) {
req.user = user;
next();
});
or use the second callback to then:
getSession(sessionId).then(function(session) {
getUser(session.user_id).then(function (user) {
req.user = user;
next();
}, function (err) {
next(new Error('user not found'));
});
}, function (err) {
next(new Error('session not found'));
});
Alternatively, the better way would to just propagate the errors through the chain, and call next only in the very end:
getSession(sessionId).catch(function (err) {
throw new Error('session not found'));
}).then(function(session) {
return getUser(session.user_id).catch(function (err) {
throw new Error('user not found'));
})
}).then(function (user) {
req.user = user;
return null;
}).then(next, next);
Since you're using bluebird for promises, you actually don't need a catch statement after every function. You can chain all your thens together, and then close the whole thing off with a single catch. Something like this:
getSession(sessionId)
.then(function (session) {
return getUser(session.user_id);
})
.then(function (user) {
req.user = user;
next();
})
.catch(function(error){
/* potentially some code for generating an error specific message here */
next(error);
});
Assuming the error messages tell you what the error is, it's still possible to send an error specific message like 'session not found' or 'user not found', but you'll just have to look into the error message to see what it gives you.
Note: I'm sure you probably have a reason for calling next regardless if there's an error or not, but it might be useful to throw in a console.error(error) in the case that you get an error. Alternatively, you could use some other error handling function, whether it's a console.error, or res.send(404), or something of the like.
I am using it like that:
getSession(x)
.then(function (a) {
...
})
.then(function (b) {
if(err){
throw next(new Error('err msg'))
}
...
})
.then(function (c) {
...
})
.catch(next);