I want to be able to reroute the website user if an error occurs in adding something to the database (a server-side validation error, like a string containing a non alphanumeric symbol or something). Currently the try-catch block doesn't catch anything even when the model.create() fails. How can I rewrite this to make it get out of the try-block and reroute back to '/' if an invalid entry is attempted?
Here's what I have (btw, Request and Ride1 are the model names):
/* POST request */
router.post('/', function(req, res, next){
/*a bunch of variable initializations to get form data*/
try{
Request.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
.catch(err => {
throw err
})
Ride1.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
.catch(err => {
throw err
})
res.redirect('/thankYou')
}
catch (e){
console.log(e);
res.redirect('/');
}
});
The .catch(err =>...) stuff doesn't seem to do anything and was just a feeble attempt at getting this to work. I think I could do it if I didn't need to Any advice is much appreciated.
Use async/await and the express-async-wrap package:
const wrap = require('express-async-wrap')
...
router.post('/', wrap(async function(req, res, next){
try{
await Request.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
await Ride1.create({
serial_num: serial_num,
meter_reading: meter_reading,
request_type: request_type
})
res.redirect('/thankYou')
}
catch (e){
console.log(e);
res.redirect('/');
}
}
));
Related
When I try to follow a tutorial. He is using a library called express-async-handler to handle async functions automatically
Link to github npm
The thing is that I try to convert that code into normal code without using library I see some different.
Here is the code with asyncHandler
router.get('/', asyncHandler(async (req, res) => {
const products = await Product.find({});
throw new Error('Error')
res.json(products);
}))
As you can see there is an error thrown to the routes. When using asyncHandler the request status is change to 500 by middleware when error is thrown
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode
res.status(statusCode)
res.json({
message: err.message,
})
}
But when I try to use the normal trycatch block, The throw is not detected by the error handler middleware
router.get("/", async (req, res) => {
try {
const products = await Product.find({});
throw new Error('Something went wrong!');
res.json(products);
} catch (err) {
res.json({ message: err });
}
});
So what the difference between the codes , from my understanding the code i converted is correct. Is there something wrong?
The problem is that you are not calling the next function with your error. Right now, you're catching the error, and simply setting the json on the response.
This means that nothing outside of this function knows about the error, since it's already been completely handled!
See if the following gets you more what you're looking for:
router.get("/", async (req, res, next) => {
try {
const products = await Product.find({});
throw new Error('Something went wrong!');
res.json(products);
} catch (err) {
next(err)
}
});
Background: The simplified test code below uses Express and Mongoose.
Question: I set up the .then statement to throw an error for testing. When an exception is thrown my error handling middleware is triggered with next() but not before res.render('index', { doesUserExist }); is hit. This line results in the error, "Cannot set headers after they are sent to the client" because in my error handling middleware res.render('error_page', { err }); is also called. What part of my code should I change to eliminate the error?
Followup: Do I need more than a slight shift in my approach? Am I using the completely wrong pattern to perform this action efficiently/effectively?
app.get('/', function(req, res, next) {
(async function() {
let doesUserExist = await User.exists( { name: 'steve' })
.then( function(result) {
throw 'simulated error';
})
.catch( function(error) {
next(new Error(error));
});
res.render('index', { doesUserExist });
})();
});
app.use(function(err, req, res, next) {
res.render('error_page', { err });
});
This is because of an async function without a catch block
app.get('/', function (req, res, next) {
(async function () {
try {
let doesUserExist = await User.exists( { name: 'steve' });
if (doesUserExist) {
throw 'simulated error';
} else {
next(new Error(error));
}
res.render('index', { doesUserExist });
} catch (err) {
return next(err)
}
})();
});
app.use(function (err, req, res, next) {
res.render('error_page', { err });
});
Instead of next write return next(new Error(error)). In this way it wont execute any further code and go to the error middleware
You can create a function wrapper to catch all the errors and send them to the error middleware:
const asyncWrap = fn =>
function asyncUtilWrap (req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
Then you can reutilize it in all your controllers, making the app much cleaner:
app.get('/', asyncWrap(async function(req, res, next) {
let doesUserExist = await User.exists( { name: 'steve' }) //*
.then( function(result) {
throw 'simulated error'; // This error is automatically sent to next()
})
.catch( function(error) {
next(new Error(error)); // This error works normally
});
res.render('index', { doesUserExist });
});
*You shouldnt combine await and then/catch syntax by the way.
I am cleaning my code and moving from callback hell to async/await and try/catch but I still want to make my code DRY as I have too many routes and performing same try catch in every request. What could be the best way to handle this?
this my example code in one of the GET route.
router.get('/customer', async (req, res, next) => {
try {
const customer = await Customer.find({}).populate('buisness').exec();
return res.status(200).json({
result: customer
});
} catch (e) {
return next(e);
}
});
now if I repeat same thing on every route it is not following DRY code. what could be the best?
const errorHandlerMiddleware = async (req, res, next) => {
try {
await next();
} catch (err) {
// handle the error here
}
};
router.use(errorHandlerMiddleware);
router.get('/customer', async (req, res, next) => {
const customer = await Customer.find({}).populate('buisness').exec();
return res.status(200).json({
result: customer
});
});
use errorHandlerMiddleware before all your routes, this middleware will catch every exception that is thrown from your routes.
now every time there is an exception in your route it will be caught in the middleware
I am new to javascript and I need to handle constraint error in sequelize. I searched related to this topic everywhere, but still, I couldn't get a proper workable answer. My attempt it as follows.
app.post('/api/users', (req, res) => {
try {
console.log(req.body);
User.create(req.body)
.then(user=> res.json(user));
} catch (error) {
console.log("Error: "+error);
}});
Here couldn't catch the exception yet. For a valid user input it is able to post the request. So I just need to know a way to handle the exception.
Was looking around for an answer for this, but was not really satisfied with the two given. If you are looking to return a correct response such as a 403 this is the solution I have came up with.
app.post('/api/users', async (req, res) => {
try {
console.log(req.body);
var user = await User.create(req.body)
return res.status(200).json({ status: 'success', result: res.json(user) })
} catch (error) {
if (error.name === 'SequelizeUniqueConstraintError') {
res.status(403)
res.send({ status: 'error', message: "User already exists"});
} else {
res.status(500)
res.send({ status: 'error', message: "Something went wrong"});
}
}
});
You can use Promise with .then().catch(), or use async/await with try/catch
This is Promise
app.post('/api/users', (req, res) => {
console.log(req.body);
User.create(req.body)
.then(user=> res.json(user))
.catch(err => console.log(err))
});
This is async/await
app.post('/api/users', async (req, res) => {
try {
console.log(req.body);
const user = await User.create(req.body);
res.json(user);
} catch (error) {
console.log("Error: "+error);
}
});
Looks like you're mixing two different styles. If you're using then(), then the try catch block is unnecessary:
app.post('/api/users', (req, res) => {
console.log(req.body)
User.create(req.body)
.then(user => res.json(user))
.catch(error => console.log('Error: ' + error))
})
The other style would be using the async package. With async, your code would look like this:
app.post('/api/users', async (req, res) => {
console.log(req.body)
try {
const user = await User.create(req.body)
res.json(user)
}
catch (error) { console.log('Error: ' + error) }
})
Both methods have their advantages and disadvantages that go beyond this snippet and lot of people use both as appropriate, for example the await approach works only inside a function declared with async like in the second example :async (req, res). In such cases, using then() style promise handling is a better approach
I have a sequelize database that validates data and throws errors.
I know I can do something like this to catch and output my errors:
User.build()
.catch(Sequelize.ValidationError, function (err) {
// respond with validation errors
return res.status(422).send(err.errors);
})
.catch(function (err) {
// every other error
return res.status(400).send({
message: err.message
});
But I don't want to add it to every single request, is there some generic way to catch theese errors?
You can add a custom method to req (or res) that will resolve the promise and handle any errors:
app.use((req, res, next) => {
req.resolve = (promise) => {
return promise.catch(Sequelize.ValidationError, err => {
// respond with validation errors
return res.status(422).send(err.errors);
}).catch(err => {
// every other error
return res.status(400).send({ message: err.message });
});
});
next();
});
Usage (provided that the middleware above is added before your routes):
router.post('/user', (req, res) => {
req.resolve(User.build()).then(user => res.json(user));
});
ES.next version (2016):
you can use async functions that throw using this wrapper function copied from the official strongloop website:
let wrap = fn => (...args) => fn(...args).catch(args[2]);
then make the function in your router/controller like that:
router.post('/fn/email', wrap(async function(req, res) { ...throw new Error(); }
and finally have a normal catch all errors middleware:
app.use(function(err, req, res, next) { console.log(err); }
Obviously for this to work you need the babel transpiler currently