This question already has answers here:
Handling errors in express async middleware
(6 answers)
Closed 3 years ago.
I google for it and found some examples (like this).
I done everything like there, and all just fine!
But now every my router function contains try...catch block. Like that:
accounts = express.Router()
accounts.post('/following', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
accounts.post('/followers', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
accounts.post('/posts', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
accounts.post('/pizza', async (req, res, next) => {
try {
***do some stuff***
if (smth_bad)
next(new ErrorHandler(413, 400, "Wrong data"));
} catch (e) {
next(e)
}
});
app.use('/api/v1/account', accounts);
app.use((err, req, res, next) => {
handleError(err, res);
});
I know, that i can use next() without try...catch, but I want to handle unexpected errors and tell about it to user. My handling looks like this:
class ErrorHandler extends Error {
constructor(statusCode, httpStatus, message) {
super();
this.statusCode = statusCode;
this.httpStatus = httpStatus;
this.message = message;
}
}
const handleError = (err, res) => {
if(err instanceof ErrorHandler){
const { statusCode, message, httpStatus } = err;
res.status(httpStatus).json({
status: "error",
statusCode,
message
});
} else {
console.error(err);
res.status(500).json({
status: "error",
statusCode: '510',
message: 'Server error',
});
}
};
Is there a way to simplify try...catch blocks in every router?
First off, try/catch will not catch errors asynchronously. What you're doing WILL work if all your code inside the try/catch is synchronous, but as you've already discovered, that's a really cumbersome way to approach the problem.
There are quite a few ways you could go about this, but I think maybe you should be asking a different question: "What does good modular code look like?" or "How do I implement a router inside my code?"
I would suggest you go looking for patterns on how to structure a router.
One possible approach is to write functions that you define outside your try/catch blocks (possibly in another module, for tidiness's sake) and use them inside the try/catch. Like this:
const getStuffFunction = function(req,callback){
//do stuff here
}
accounts.post('/pizza', async (req, res, next) => {
getStuffFunction(req,function(){
//do router stuff here after your business logic is done
})
Hope that helps.
Related
There is a lint error, that basically says that we cannot return Promises in places where a void is expected, the message is clear, but how to fix it without lying to the linter by using any or (as any), both functions [validateJWT, getUser] are async functions It looks pretty basic, but I do not know how to fix it in an easy way. thanks!
import { Router } from 'express';
import { getUser } from '../controllers/userController';
import { validateJWT } from '../middlewares/validateJWT';
const router = Router();
router.get('/user', validateJWT, getUser);
export default router;
const getUser = async (req: Request, res: Response, next:
NextFunction): Promise<any> => {
try {
const { id } = req.params;
if (!id) {
let response = formatErrorResponse({ error: true, statusCode: 400, errorMessage: 'Missing id in params' });
return res.status(400).json(response);
}
let user = await User.findById({_id: id});
let objRes = { error: false, statusCode: 200, message: 'User was found', data: user };
return res.status(200).json(objRes);
} catch (error) {
console.log('error in controller /user', error)
next(error);
}
}
export {
getUser
}
const validateJWT = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
const token = req.header('x-token');
console.log(req)
if (!token) {
const err = formatErrorResponse({ error: true, statusCode: 401, errorMessage: 'Missing header x-token' });
return res.status(401).json(err);
}
try {
await verifyToken(token);
next();
} catch (error) {
const err = formatErrorResponse({error: true, statusCode: 401, errorMessage: 'Invalid token, athorization denied', errorContent: error});
return res.status(400).json(err);
}
}
You need to change your implementation of the function to work like this:
router.get('/user', (req, res) => {
validateJWT(req, res);
getUser(req, res);
});
Since express.Router instances expect the route as a string for the first parameter which you have as "/user". And a second parameter which is a callback.
Inside that callback you can call your functions.
Assuming you need validateJWT to finish running before getUser you could do something like this:
validateJWT(...).then((...) => getUser(...));
I suggest something like the below.
router.use(validateJWT)
router.get('/users/:id', (req, res, next) => {
getUser(req.params.id)
.then(res.json)
.catch(next);
});
In the docs, I could see an async middleware example http://expressjs.com/en/guide/writing-middleware.html#mw-fig.
I could not find an async handler example. I recall that back in the day, express didn't support async handler. Hence, I used .then inside the handler. You need to double-check if you can use them nowadays.
In the express docs is also an example like the below. It makes working with async handler more convenient in some regard. As you can wrap all your async handler with this wrapper.
const wrap = fn => (...args) => fn(...args).catch(args[2]);
router.get('/users/:id', wrap(getUser));
I have an API in Node.js. My routes look like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(next);
};
As seen in the example, I use .catch(next). But is this the correct way to do it? Shouldn't the route always print json?
So I am thinking of doing something like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(err => {
res.json({
status: 'error',
msg: err
});
});
};
but shouldn't it then be something like res.status(some_status_code).json({})?
How is a simple API normally carried out in terms of error handling?
What if I, in the code, use a variable that is not defined (i.e. causing a syntax error)? Should I handle it with a JSON error or should I just make sure that I don't do sloppy coding? :-D
Also, is this the fastest way to print the json? I mean, should I use User.find().lean()? Should I do some caching? Is it even clever to store my API on a normal website or are there optimized API servers for such cases?
Have you try async/await function and custom response for success and error?
Here the example :
responseHandler.js
// use this for success
function successResponse(message, data) {
const success = {
success: true,
message: message,
data: data
}
return success
}
// use this as middleware error handler
function errorResponse(err,req,res,next) => {
return res.status(err.status || 500).json({
success: false,
message: err.message
})
}
module.exports = {
successResponse,
errorResponse
}
myRouter.js
const { successResponse } = require('./responseHandler')
exports.getUserList = async (req, res, next) => {
await User.find().sort({ name: 1 }).then(users => {
res.status(200).json(
successResponse(`data user`, users)
)
}).catch(err => {
return err
});
};
I'm using promises inside express middleware. I want to use the async/await methods.
app.get('/data1',async function(req,res) {
data = await getData1(); // This line throw an error,
res.send(data)
})
app.get('/data2',async function(req,res) {
data = await getData2(); // This line throw an error
res.send(data)
})
This makes the browser wait forever.
On the server I see
(node:251960) UnhandledPromiseRejectionWarning: Unhandled promise rejection
Now, to fix it for one middleware I'm doing:
app.get('/data1',async function (req,res){
return (async function(){
data = await getData1()
})().catch(() => {
res.send("You have an error")
}
})
app.get('/data2',async function (req,res){
return (async function(){
data = await getData2()
})().catch(() => {
res.send("You have an error")
}
})
I don't like this repetion. How can I set default error? I have tried for example:
app.use(function(error,req,res,next)){
res.send('You have an error')
}
But it didn't work.
In other words: How to set default function to be called when Express middlewares returning a rejected promise?
Now I found a way how to do it, I'm still keep the question open for more suggestions
app.get("/data1",
wrap_middleware(async (req, res) => {
data1=await getData1()
res.send(data1)
})
}
app.get("/data1",
wrap_middleware(async (req, res) => {
data2=await getData2()
})
}
function wrap_middleware(func) {
return async (req, res, next) => {
func(req, res, next).catch(err => {
console.log(err.message);
res.send("Error");
});
};
}
I don't understand the use of sending the same error for different function but I think the handling error code could be write in more readable way (just catch the error and do with them what you want the same way you catch errors in any route middleware):
function getData1(){
return new Promise( (resolve,reject) => {
setTimeout(() =>{
reject(new Error('error has occur!'));
},2000);
});
}
router.get('/data1', async (req,res, next) => {
try{
const data = await getData1();
res.send(data);
}
catch(ex){
res.send(ex.message);
// next(ex); => sending it to express to handle it
}
});
If you want a global error handling then its not any different from any code you want catch errors globally - you can set a function that take as param , the response object and the async code and create general catch for every async call comes from middleware (which has response object)
I have some error handling middleware defined and a route returning a promise. But when that promise gives an error, I have to manually append .catch(err => next(err)) after every promise. While its not a problem, isn't it sensible for ExpressJs to see if a route returns a promise and if so call the error handling middleware automatically.
My current shortened code:
// errorHandlers.js
function sequelizeValidationError (err, req, res, next) {
if (err.name && err.name == 'SequelizeValidationError')
res.status(400).send(err.errors)
else next(err)
}
// auth.js
router.post ('/register', middleware.isNotAuthenticated, (req, res, next) => {
const { email, password, name } = req.body;
return models.User.find({where : { email }}).then(user => {
if (user) {
if (user.password == password) sendToken(user.id, res);
else res.sendStatus(401);
} else {
return models.User.create({
email, password, name
}).then(user => {
sendToken(user.id, res);
})
}
}).catch(next)
})
// index.js
router.use('/auth', require('./auth'))
router.use(errorHandlers.sequelizeValidationError)
For example, currently I could have forgot to write catch at one place and the server would have failed.
Am I missing out on something? How can I avoid having to type the catch every time?
This is already filed.
I had filed a duplicate bug
As of now the best bet seems to be to use a wrap function .
Also see #robertklep 's comment above. promise-express-router may be useful if you donot use route-params . express-co seems to be a wrap function + more generator-based goodness
This doesn't show any response to an HTTP request, just hangs:
function middleware(req, res, next) {
to_end(res).NotFound();
return next();
}
This works:
function middleware(req, res, next) {
to_end(res).NotFound();
res.send(); // <----------------------------------------------
return next();
}
I'm using restify with this simple function:
export const to_end = res => {
return {
NotFound: (entity = 'Entity') => res.json(404, {
error: 'NotFound', error_message: `${entity} not found`
})
}
}
I'm not so good at ES6 but I guess that it may be the javascript's context problem (I mean the this variable).
In your case, res.json() may throw res is not defined (I guess) since the this at that moment is the context of export const to_end ... (thanks to =>'s magic), which does not know what is res.
Sorry for my terribly bad explanation :(. Read this to understand more about this.
We can make things simpler:
module.exports = function(res) {
return {
NotFound: function() {
res.json(404, {
error: 'NotFound',
error_message: `${entity} not found`
});
}
}
};
P/S: Not tested yet. Good luck! :)