I need to handle errors in my API server on node.js. I created a error handling module, which sends the errors (only in dev mode) to the API client in a JSON object similar to this:
{ "status": "fail", "error": { "statusCode": 404, "status": "fail", "isOperational": true }, "message": "no valid register found. Please provide a valid register", "stack": "Error: no valid register found. Please provide a valid registern at /Users/myUser/ITstuff/smarthome-heating/controllers/modbusController.js:77:21n at async /Users/myUser/ITstuff/smarthome-heating/controllers/modbusController.js:73:17" }
now the problem I have is, that I need to create a new error in a submodule of my API controller when no case is true in the switch statement.
// this is the modbusHandler.setValue(doc, val, next) function // 2) RUN PROGRAMMS let checkVal; switch (doc.register) { case 0: await client.writeCoil(doc.address + offset, !!val); checkVal = await client.readCoils(doc.address + offset, 1); break; case 4: await client.writeRegister(doc.address + offset, val); checkVal = await client.readHoldingRegisters(doc.address + offset, 1); break; default: throw new Error( 'no valid register found. Please provide a valid register' ); }
At this time, I handle it in this way:
// the function is wrapped in a try-catch statement const val = await modbusHandler.setValue(doc, req.body.value, next).catch((err) => { console.log('here it is'); return next(new AppError(err.message, 404)); }); res.status(200).json({ status: 'success', data: { val, }, });
- In the API controller, the function with the switch statement gets called
- If no case matches the expression, then it throws a new Error
- catch the error then call the custom error and respond error in a JSON format to the client
This solution works not really, I get a response error because there is no return for the function of the API controller. This causes a second response which is of course not good.
My question is now: How can I solve this problem in the right way?
Custom Error constructor:
class AppError extends Error { constructor(message, statusCode) { // this is the official error message rom the error it self, this message will be in the response.json for the client super(message); this.statusCode = statusCode; this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error'; this.isOperational = true; Error.captureStackTrace(this, this.constructor); } } module.exports = AppError;
Custom Error handler:
module.exports = (err, req, res, next) => { err.statusCode = err.statusCode || 500; err.status = err.status || 'error'; if (process.env.NODE_ENV === 'development') { sendErrDev(err, res); } else if (process.env.NODE_ENV === 'production') { sendErrProd(err, res); } }; const sendErrDev = (err, res) => { res.status(err.statusCode).json({ status: err.status, error: err, message: err.message, stack: err.stack, }); };
Advertisement
Answer
Instead of throwing the error, perhaps want to throw it to next like this so it can be handled by the error handler.
export const yourFunction = async (req, res, next) => { ..... // throw the error to your route Error handler return next(new AppError('no valid register found. Please provide a valid register', 400)) }
Then after all your route declaration you should have an Error handler that looks something like.
app.use(ErrorHandler);
You may also need an error catcher
//catchAsync module.exports = fn => { return (req, res, next) => { fn(req, res, next).catch(next); }; };
Which you will wrap your route like this.
route.get('/path/', catchAsync(yourFunction))
If you create the catchAsync middleware, you will not need any Try/Catch in your routes at all, as all will be thrown to your error handler.
Update.
The small complain about node, or even javascript is about the error handling. For every function of function of function called, if you choose to throw error, then you need to throw error of error of error. You keep bubbling up “throw error” and then it gets out of hand.
In your switch statement, I would recommend you to return a null.
then you test `if (!variable) return next(new AppError);
Helper functions should behave like a helper function, it should return true/false/null/value, and then you determine in your main function whether an error should be thrown or not.
This way you centralises your error.