Can someone expound on the times when it's appropriate in a node.js Express app to throw an error like so:
throw new Error('my error');
or to pass this error on via the callback usually labelled 'next' like so:
next(error);
and could you please explain what each of them will do in the context of an Express app?
for example, here is an express function dealing with URL parameters:
app.param('lineup_id', function (req, res, next, lineup_id) {
// typically we might sanity check that user_id is of the right format
if (lineup_id == null) {
console.log('null lineup_id');
req.lineup = null;
return next(new Error("lineup_id is null"));
}
var user_id = app.getMainUser()._id;
var Lineup = app.mongooseModels.LineupModel.getNewLineup(app.system_db(), user_id);
Lineup.findById(lineup_id, function (err, lineup) {
if (err) {
return next(err);
}
if (!lineup) {
console.log('no lineup matched');
return next(new Error("no lineup matched"));
}
req.lineup = lineup;
return next();
});
});
In the line commented "//should I create my own error here?"
I could used "throw new Error('xyz')", but what exactly would that do? Why is it usually better to pass the error to the callback 'next'?
Another question is - how do I get "throw new Error('xyz')" to show up in the console as well as the browser when I am in development?
In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next', also an error handler needs to be defined so that all the errors passed to 'next' can be handled properly.
http://expressjs.com/en/guide/error-handling.html
Throwing an error inside a callback doesn't work:
app.get('/', function (req, res) {
fs.mkdir('.', (err) => {
if (err) throw err;
});
});
But calling next works:
app.get('/', function (req, res, next) {
fs.mkdir('.', (err) => {
if (err) next(err);
});
});
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:
app.get('/', function (req, res) {
throw new Error('BROKEN') // Express will catch this on its own.
})
For those who prefer throwing errors, here is a workaround decorator:
export function safeThrow(
target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<(req: Request, res: Response, next: NextFunction) => Promise<any>>) {
const fun = descriptor.value;
descriptor.value = async function () {
try {
await fun.apply(this, arguments);
} catch (err) {
arguments[2](err);
}
};
}
#safeThrow
private async get(req: Request, res: Response, next: NextFunction) {
throw { status: 404, message: 'Not supported' }
}
Related
My problem is that for some reason express router doesn't wait for next() function inside middleware and goes str8 to execution fo router.post.
Router.use('/posts/add', addPosts);
Router.post('/posts/add', (req, res) => {
if(req.success){
res.status(200).send('post added');
}else{
res.status(400).send({error: true, message: 'sth went wrong'})
}
});
Below is the middleware code:
module.exports = (req, res, next) => {
try {
if (req.authorization && req.authorization.access_level < 3) {
let post = new postsModel(req.body);
post.save().then(post => {
console.log(post);
req.success = true;
next();
});
} else {
throw new Error('unauthorized access');
}
} catch (err) {
res.status(400).send({ error: true, message: err.message });
}
};
Despite middlewares execution, router for some reason is always executing the router.post, doesn't wait for the next() function therefore awlays return error. Anybody could help with that?
Maybe try with the following approach:
Router.route('/posts/add', addPosts)
.post((req, res) => {
Refer to this documentation for further info.
I wanted to deeply apologize as I haven't posted the code for 1 more middleware invoked before anything else.
try {
const path = req.path;
switch (path) {
case '/add':
let data = req.body;
let error = addPostSchema.validate(data).error;
console.log(error);
if (error) {
throw new Error(error.message);
} else {
console.log('addPost validated');
next()
}
}
next();
} catch (err) {
console.log('walalala');
res.status(400).send({ error: true, message: err.message });
}
};
As you can see above, I've had double next() invoked. What I didn't know is that invoking next() does not stop rest of the code in middleware from executing, therefore it was prematurly invoking Router.post. Apologies for the confusion. Hope it will help somebody though having similiar problem.
I have a problem and I haven't found a solution yet. I want to catch all errors if occur any error in each route but it's very inconvenient when I have to do it many times.
How can i implement it as a middleware same like app.use(ErrorHandle); ?
Code in ErrorHandler.js:
export const ErrorHandler = func => async (req, res, next) => {
try {
await func(req, res, next);
} catch (error) {
next(error);
}
}
Code in index.js
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
Code in route need to catch error:
import { ErrorHandler } from './ErrorHandler';
export const uploadMedia = ErrorHandler(async (req, res) => {
// do something...
let error = new Error();
error.statusCode = 404;
error.message = 'Content not found!';
}
Sorry if misunderstood your question... When you do the code below which you provided, you are assuming that if an error reaches the end of the stack via next(err), such handler should be called. Hence it's the last declaration after all your routes.
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
That, however, won't catch unhandledExceptionErrors. You still need the good old
try {
// throw error somewhere
} catch (e) {
next(e);
}
Personally, I haven't tried this package but it seems so be a nice hack for Express router's inability to handle promise returns. About express-promise-router:
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.
I a trying to implement a rest API for our project then I go for node js and express. I have built all the models and controllers. I faced an issue while trying to handle an error. Errorhandler function doesn't receive all the properties of error that caught in try/catch block. I can not read its name in a handler but I can use its name in the controller. Could you please help me?
const errorHandler = (err, req, res, next) => {
console.log(`Error in method:${req.method}: ${err.stack}`.bgRed);
let error = { ...err };
console.log(`Error handler: ${err.name}`);
res.status(error.statusCode || 500).json({
success: false,
data: error.message || 'Server Error',
});
};
module.exports = errorHandler;
controller
const mongoose = require('mongoose');
const Product = require('../models/Product');
const ErrorResponse = require('../utils/error');
const routeName = 'PRODUCT';
// #desc getting single product via id
// #route GET api/v1/products
// #acces public
exports.getProdcut = async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return next(
new ErrorResponse(`Product not found with id:${req.params.id}`, 404)
);
}
res.status(200).json({
success: true,
data: product,
});
} catch (err) {
console.log(err.name);
console.log('ERRO APPEND');
next(new ErrorResponse(`Product not found with id:${req.params.id}`, 404));
}
};
Assuming that errorHandler is part of your middleware that is somewhere after getProdcut, you can try just throwing the error and Express will automatically detect that for you, because error handling middleware such as yours accepts 4 parameters. So the following would work:
const getProdcut = async (req, res, next) => {
try {
// ...
} catch (err) {
throw err;
}
};
const errorHandler = (err, req, res, next) => {
if (err) {
console.log('hello from the error middleware');
console.log(err.name);
}
else {
// next() or some other logic here
}
}
app.use('/yourRoute', getProdcut, errorHandler);
And inside of your errorHandler you should have access to the error object.
Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors.
https://expressjs.com/en/guide/using-middleware.html#middleware.error-handling
I'm using Express and I want to use the build-in middlewares to handle errors.
I'm using it this way -
app.use((err,req,res,next)=> {
console.log('error');
)};
Now this middleware is reached only if I'm using next(error) (where error is object of type Error) in my router.
On the other hand, if I simply throw new Error() in my router, the middleware is not called.
Is there any way to omit the next()?
So that if any error is thrown in my router, it will be handled by the middleware?
If you can use Node.js >= 8 that comes with async/await then you can use the following wrapper function over your routes:
const wrap = fn => {
return async function(req, res, next) {
let e = null;
try {
await fn(req, res, next);
} catch (err) {
e = err;
next(err);
}
if (!e) {
next();
}
};
};
app.get(
"/path",
wrap(async (req, res) => {
// use sync style code here which can throw
})
);
If the throw is synchronous, you could invent your own request handler wrapper that would catch any exceptions and turn them into a call to next():
app.get2 = function(path, fn) {
app.get(path, function(req, res, next) {
try {
fn(req, res, next);
} catch(err) {
next(err);
}
});
};
And, sample usage:
app.get2("/test", function(req, res, next) {
throw new Error("testing..."); // will end up in next()
});
Then, if your fn function throws synchronously, then it will be caught by your get2() wrapper and will automatically call next(err) for you.
If you're throwing asynchronously, this won't work. There is no generic wrapper for asynchronous throws because throws into an asynchronous callback don't throw to your code - they throw back into the asynchronous infrastructure for whatever async operation you were using and only they can catch it. If you use promises exclusively for your asynchronous operations, then the promise infrastructure will automatically catch your throws and turn them into rejected promises and you could do something similar to above for a returned and rejected promise. The Koa framework (a derivative of Express) does something similar already and I think Express 5 will have some features like that to.
I have a helper function that hits an API and fetches a page by ID. It uses async/await and I am trying to handle errors with try catch.
To test the error handling I am purposefully giving it an ID that does not exist.
Here is the method:
const findPage = async (req, res, pageId) => {
let document
try {
let response = await getByID(pageId)
if (!response) throw Error('No Response')
return document
} catch (error) {
console.log(error) // I can see the error is being thrown.. I am purposefuly giving it an id that does not exist
return error
}
}
It does indeed throw an error like I expect. However, I am calling the function in another part of the app using an express Route.
Router.route('/page/:id').get(async (req, res) => {
let results
try {
results = await findPage(req, res, req.params.id) // This Function Returns an error
// Yet we still get results
res.json({results, msg: 'WHY?'})
} catch (error) {
res.send(error)
}
})
In the same Router file, I have attempted to add some specific middleware to this Router as well, but as there is no error, it is never triggered.
Router.use((err, req, res, next) => {
if (err) {
console.log('holy error')
} else {
console.log('no error')
}
next(err)
})
How can the express API call return results, and not the error, when the function it is calling itself returns an error?
What I can see from your code is that your try/catch block inside the Router.route is not catching an expetion of findPage function, and that is becasue you are also catching an exception inside that findPage and just simply returning an error rather than throwing an exception;
try {
results = await findPage(req, res, req.params.id) // This Function Returns an error
// Yet we still get results
res.json({results, msg: 'WHY?'})
} catch (error) {
res.send(error)
}
So inside findPage if you really need to do something when excptions happening then you have to catch it and if you like the calller also catch the expetion you need to throw the same erro or a new more context-aware error again. Otherwise if you dont do any action when exception happens you dont need to catch it.
const findPage = async (req, res, pageId) => {
let document
try {
let response = await getByID(pageId)
if (!response) throw Error('No Response')
return document
} catch (error) {
// do something here if you really need to otherwise you dont need to catch exceptions here
// then rather than `return error` you should
throw error
}
}
You are catching the error in findPage() which means the error won't propagate up the call stack. You are just returning the error like a normal value, which will end up in the variable results in your Routes function. If you want to deal with the error in both places you need to throw it again in findPage():
async function callee(){
try {
throw("Some Error")
} catch(err) {
console.log("caught error in callee:", err)
throw(err)
}
}
async function caller(){
try {
let val = await callee()
console.log("returned value", val)
} catch (err) {
console.log("caught error in caller: ", err)
}
}
caller()
If you don't want to deal with it on both places, catch it in the function that's responsible for handling errors:
async function callee(){
throw("Some Error")
}
async function caller(){
try {
let val = await callee()
console.log("returned value", val)
} catch (err) {
console.log("caught error in caller: ", err)
}
}
caller()