18

So i'm using express.js and looking into using async/await with node 7. Is there a way that I can still catch errors but get rid of the try/catch block? Perhaps a function wrapper? I'm not sure how this would actually execute the function's code and also call next(err).

exports.index = async function(req, res, next) {
  try {
    let user = await User.findOne().exec();

    res.status(200).json(user);
  } catch(err) {
    next(err);
  }
}

Something like this...?

function example() {
   // Implements try/catch block and then handles error.
}

exports.index = async example(req, res, next) {
  let user = await User.findOne().exec();
  res.status(200).json(user);
}

EDIT:

Something more similar to this:

var wrapper = function(f) {
    return function() {
        try {
            f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

This would somehow handle the try/catch block but doesn't work:

exports.index = wrapper(async example(req, res, next) {
  let user = await User.findOne().exec();
  res.status(200).json(user);
});

See Is there a way to add try-catch to every function in Javascript? for the non-async example.

Community
  • 1
  • 1
Neverlax
  • 405
  • 2
  • 5
  • 15
  • 1
    Just use express-promise – Bergi Dec 27 '16 at 18:05
  • 1
    You can't wrap an await without making the wrapping function be async. In this case, why not just use `.then()` and `.catch()`. What is `await` buying you. Error handling needs to be there. You can't just wish it away to some other function. – jfriend00 Dec 27 '16 at 18:08
  • I'm looking at ways to remove nested `then` statements when they may need to fork. The above code is just there as a demonstrative example. – Neverlax Dec 27 '16 at 18:15
  • 1
    @Neverlax - You need to be more specific what you're asking then. There need be no nested `.then()` handlers in what you have above - you can just chain if there's a need for more than one async operation to be sequenced. You can't hide error handling and trying to is a fool's errand. A big downfall of `await` is that people tend to just shortchange error handling whereas the need for it is more obvious with `.then()` and `.catch()`. You can still do it with `await`, but it needs `try/catch. Please use real-world code that is as complicated as what you're trying to ask about. – jfriend00 Dec 27 '16 at 18:50
  • @jfriend00 Appreciate the comment. I feel its clear when i ask `Is there a way that I can still catch errors but get rid of the try/catch block scaffold` – Neverlax Dec 27 '16 at 18:58
  • FYI, `async/await` is not part of ES7. – Felix Kling Dec 30 '16 at 17:43
  • There's a great article about this topic with examples here: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 – omer May 21 '19 at 09:45

6 Answers6

21

Yes, you can easily write such a wrapper for asynchronous functions as well - just use async/await:

function wrapper(f) {
    return async function() {
//         ^^^^^
        try {
            return await f.apply(this, arguments);
//                 ^^^^^
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

Or you use promises directly, like in this example that is more tailored to express (especially with the number of parameters):

function promiseWrapper(fn) {
    return (req, res, next) => {
         fn(req, res).catch(next);
    };
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    This does work. Though, async needs to be applied to the returned function instead of the actual wrapper function. Could you make that edit so that i can mark it is the answer? thanks! – Neverlax Dec 27 '16 at 19:21
  • Yes, it's an arrow function, but a plain function would've worked as well. What is important is that they are formally declared parameters, i.e. `promiseWrapper(…).length == 3`, which express uses to distinguish handlers. – Bergi Dec 27 '16 at 19:55
  • Can you please explain this for me as I am very new to Express and Javascript and finding it hard to understand how this works. I did not get the fact that we are returning a function which is executing the original function (fn) but how does this execute in practice – ankshukla Feb 16 '22 at 17:31
  • @ankshukla "*the fact that we are returning a function which is executing the original function*" - it seems like you did get it :-) Not sure what else there is to say. You might want to learn about closures in general? Or are you not sure how `wrapper` is applied? – Bergi Feb 16 '22 at 17:48
2

If someone prefers async/await and Express-specific approach, following snippet could be useful

export function asyncWrap(fn) {
  return async function wrappedFn(req, res, next) {
    try {
      await fn(req, res);
    } catch (err) {
      next(err);
    }
  };
}

it can be used in the router in the following way

customRouter.get('/', asyncWrap(customController.getCustomListHandler));
Željko Šević
  • 3,743
  • 2
  • 26
  • 23
2

So, async functions are actually promises, I came up with this solution:

const asyncWrapper = async promise => {
    try {
        return [null, await promise];
    } catch (err) {
        return [err];
    }
};

const yourAsyncMethod = params => new Promise((resolve, reject) => {
    resolve(params);
});

(async () => {
  // Wrap the executed async method but use the await outside
  const [error, result] = await asyncWrapper(yourAsyncMethod(1));
  
  if (error) {
    // Handle error
  }
  console.log(result);
})();
jmdiego
  • 542
  • 6
  • 15
1

A similar answer here hope can help you

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});
0
const catchAsyncErrors = (func) => (req, res, next) =>
  Promise.resolve(func(req, res, next)).catch((err) => {
    console.log("error", err);
    next();
  });

export default catchAsyncErrors;

then in controllers:

const currentUserProfile = catchAsyncErrors(async (req, res) => {
  const user = await User.findById(req.user._id);
  res.status(200).json({
    success: true,
    user,
  });
});
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
0

I'm using Firebase Functions onRequest method to serve an Express App. The other answers on this page helped me arrive at this solution for catching errors at the top level and avoid having to have a try/catch inside each handler.

The keypoints were

  • add async to the onRequest method
const admin = functions.https.onRequest(async (req, res) => adminApp(req, res));
// adminApp
const app = express();

// middleware here...

addPostHandler(app, '/createUser', createUser);
//  ...other routes

// error middleware here...
  • add await to the handler call
// addPostHandler
export const addPostHandler = (app, route: string, handler) => {
  app.post(route, async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    // try catch here means we don't need to add inside each handler
    try {
      await handler(req, res);
    } catch (error) {
      return next(error);
    }
  });
};
Kildareflare
  • 4,590
  • 5
  • 51
  • 65