I am trying to understand how to forward errors that occur in routes in a nodejs express application back to the error handler without throwing the error.
Basically I have my middleware setup like this:
const allowedMethods = ['GET', 'POST'];
app.use(function (req, res, next) {
if (!allowedMethods.includes(req.method))
return res.sendStatus(405);
});
let paymentRoutes = require('./routes/payment');
app.use('/payment', paymentRoutes);
let notificationRoute = require('./routes/notifications');
app.use('/notifications', notificationRoute);
let customerRoutes = require('./routes/customer');
app.use('/customer', customerRoutes);
let subscriptionRoutes = require('./routes/subscription');
app.use('/subscription', subscriptionRoutes);
app.use(function (err, req, res, next) {
console.log("error occured!");
console.log(err);
if (err) {
console.log("Throwing an error!");
next(err);
} else {
console.log("no error...")
}
});
app.use(function logErrors (err, req, res, next) {
winston.error(err);
next(err);
});
app.use(function sendMailToAdmin(err, req, res, next) {
//sendmailtoadmin
});
And it is working whenever I throw an error in one of my routes. Currently I am handling errors like this in my routes though.
router.get('/:id/has-premium', (req, res) => {
//throwing a test error
throw new Error;
const userId = req.params.id;
customer.hasActivePremium(userId)
.then(activePremium => {
res.status(200).send(JSON.stringify({"success": true, "premium": activePremium}));
})
.catch(err => {
res.status(err.httpCode).send(JSON.stringify({"success": false, "error": err.description}));
})
});
My question is how would I "call" my middleware error handler with the error I caught when I caught the promise?
You can use next in the route signature like
router.get('/:id/has-premium', (req, res,next) => {
//throwing a test error
throw new Error; // you can call next() here
const userId = req.params.id;
customer.hasActivePremium(userId)
.then(activePremium => {
res.status(200).send(JSON.stringify({"success": true, "premium": activePremium}));
})
.catch(err => {
res.status(err.httpCode).send(JSON.stringify({"success": false, "error": err.description}));
})
});
and then call next(err) instead of res.status(err.httpCode).send(JSON.stringify({"success": false, "error": err.description}));
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);
}
})
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.
Basicly I got an async function like such:
export default (htmlFilePath, observer, redisClient) => async (req, res, next) => {
try {
...bunch of logic...
} catch (error) {
// if i log error here it displays correctly
next(error)
}
}
So if code comes to the catch above I can correctly use the error with stackTrace etc, but when passing it with next() to go to this express function error is lost somewhere... :
.get('/*', loader(filePath, observer, redisClient))
.use(function (err, req, res, next) {
res.statusCode = 500;
// Logger only logs: TEST
logger.error('TEST', err, err.stack);
res.send("Internal Server Error");
})
Anyone know what I am doing wrong? I want to make use of the error in the function above.
I found a solution at this post, can you try this code?
function loader(req, filePath, observer, redisClient, next) {
try {
...bunch of logic...
} catch (error) {
// if i log error here it displays correctly
req.error = error;
}
next();
}
api.get('/*', loader(req, filePath, observer, redisClient))
.use(function (req, res, next) {
// Logger only logs: TEST
var err = req.error;
if(err){
logger.error('TEST', err, err.stack);
res.send("Internal Server Error");
}else{
//Do something here
}
})
I'm not a big fan of try/catch inside of controllers, error handler middleware should handle them.
From https://expressjs.com/en/advanced/best-practice-performance.html#handle-exceptions-properly
app.get('/', function (req, res, next) {
// do some sync stuff
queryDb()
.then(function (data) {
// handle data
return makeCsv(data)
})
.then(function (csv) {
// handle csv
})
.catch(next)
})
app.use(function (err, req, res, next) {
// handle error
})