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
Related
Why does this approach not work? How can I create an error gaurd middleware for my API?
export function ErrorCatcherMiddleware() {
return (req: Request, res: Response, next: NextFunction) => {
try {
next();
} catch (err) {
console.log("trying request failed")
next(err);
}
} ...
app.use(ErrorCatcherMiddleware());
// ...routes and other middlewares
}
The error handling middleware takes 4 arguments (error as first arg) as opposed to 3 arguments for regular middleware.
const handleErrors = (err, req, res, next) => {
return res.status(500).json({
status: 'error',
message: err.message
})
}
app.use(handleErrors)
I may assume that it doesn't catch errors because the code that produces them is asynchronous.
To catch these errors you need to wait until the async operation is finished and in case of error call the next(err) function.
For example
app.get('/', (req, res, next) => {
fs.readFile('/file-does-not-exist', (err, data) => {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
app.use((err, req, res, next) => {
// this middleware should be executed in case of the error
});
Or you can use middlewares that return promises like below (starting with Express 5).
app.get('/', async (req, res, next) => { // callback is "async"
const data = await fs.readFile('/file-does-not-exist');
res.send(data);
});
app.use((err, req, res, next) => {
// this middleware should be executed in case of the error
});
In this case, if fs.readFile throws an error or rejects, next will be called with either the thrown error or the rejected value automatically. You can find more details about that in this document
Update Question: error: TypeError: res.json is not a function
I use Firebase Cloud Functions with Express app. I use middleware for handle error, but it is not working. How to catch/handle error when using throw new Error()?
My code below:
app.get('/test', (req, res) => {
throw new Error('this is error')
})
function errorHandler(err, req, res, next) {
res.json({error: err.message}) // error here
}
app.use(errorHandler)
exports.api = functions.https.onRequest(app)
Please help me. Thanks very much.
I had the same issue. You need a try/catch to capture the error and then use the next function to pass it down the middleware chain.
app.get('/test', (req, res, next) => {
try {
throw new Error('this is error')
} catch (err) {
next(err)
}
})
function errorHandler(err, req, res, next) {
res.json({error: err.message}) // error here
}
app.use(errorHandler)
exports.api = functions.https.onRequest(app)
Wrap the whole handler in the try block and it will always pass it down to the error handler.
You can use try/catch to handle error like this:
app.get('/test', (req, res) => {
try {
// Your Code
} catch (e) {
console.error("error: ", e);
res.status(500).send(e.message);
}
})
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)
}
});
I am new to express (or any JS backend) so sorry if question was already answered, or kind of stupid.
I have registered endpoint.
app.get('/hello-world'), async (req, res) => {
try {
// do something
sendResponse({"Message": "Hello World"}, res);
} catch (e) {
handleError(e, res);
}
});
Where sendResponse and handleError are doing just setting status and body / additional exception metadata using res.status().json()
Is there any way to make response handling more simple by registering some response handler and write the logic of response / exception handling at one place?
What I have in mind is this:
Change example endpoint logic to:
app.get('/hello-world'), async (req, res) => {
return {"Message": "Hello World"}
// or throw new error
});
and some repsonse handler which will handle result of function
resposeHandler(payload, err, res) {
if (err) {
res.status(500).json(err) // just as an example
} else {
res.status(200).json(payload)
}
}
You can create a function wrapper to catch all the errors and send them to the error middleware:
const errorHandler = (routeHandler) =>
(req, res, next) => {
const routeHandlerReturn = routeHandler(req, res, next)
return Promise.resolve(routeHandlerReturn).catch(next)
}
Then you can reutilize it in all your controllers, making the app much cleaner:
app.get('/hello-world', errorHandler(async function(req, res, next) {
sendResponse({"Message": "Hello World"}, res);
});
If some error is thrown, it will be handled in your error handler middleware:
// index.js
app.use((err, req, res, next) => {
res.status(err.status || 500)
res.json({
message: err.message || 'Internal Error',
error: err.error
})
})
create two middlewares, one for error handle and the other one for success response
// error handling when error occured
function errorHandler(req,res,next) => {
return res.status(err.status || 500).json({
success: false,
message: err.message
});
};
// success response and return data
function successHandler(successMsg, successData) => {
return (req,res,next) => {
return res.status(200).json({
success: true,
message: successMsg,
data: successData
});
};
};
register them in express
const app = express();
app.get('/someroute', successHandler('it is endpoint of someroute!', 'your data here'))
app.use(errorHandler)
use errorHandler after you call and define the route
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.