Using Async Await in Express with Node 9
Since the arrival of node v 7.6.0 async / await has been supported in node by default. If you’re using Node 8.0+ there is no reason to not use async / await in your node applications. If you aren’t familiar with async / await or aren’t sure why you would want to use it over promises, here’s a quick rundown:
Go from this:
If you’re familiar with promises you know that if a promise is rejected you’ll need to handle that error inside a `.catch`, and if you’re handling errors for both synchronous and asynchronous code you will likely have to duplicate your error handler.
In the above snippet we can see that there is duplicate code on lines 6 and 8. The catch statement on line 7 will handle any errors that the synchronous function
doSynchronousThings may throw but it won’t handle any errors thrown by
getUsers since it is asynchronous. This example may seem palatable since all its doing is printing the error to the console, but if there is any kind of complex error handling logic we want to avoid duplicating it. Async / await lets us do exactly that:
There are many more advantages async / await has over promises and if you’re interested I encourage you to read about them here, but for now lets move on to using async / await with express.
Async / Await in Express
Implementing basic async / await functionality in express is quite straightforward. The most important thing to remember to do is to wrap functions you are awaiting in try / catch statements so you do not get silent errors.
Easy, right? Well… yes… but do we really need to write a try / catch statement inside of every route? Surely we can do better.
Wrapping Async Await Routes
Since Async Await is essentially syntactic sugar for promises, and if an
await statement errors it will return a rejected promise, we can write a helper function that wraps our express routes to handle rejected promises.
This function can be a little tricky to read but is actually quite straightforward, so lets break it down. asyncMiddleware is a function that takes another function and wraps it in a promise. In our use case the function it will take is an express route handler, and since we are passing that handler into
Promise.resolve it will resolve with whatever value our route handler returns. If, however, one of the
await statements in our handler gives us a rejected promise, it will go into the
.catch on line 4 and be passed to
next which will eventually give the error to our express error middleware to handle. Now all that remains to do is to wrap our routes in our asyncMiddleware and we will no longer have to worry about using try / catch statements in our routes.
So, our code looks much cleaner, we get the advantages of async / await, and we’re confident that all of our errors are being caught and handled! An important thing to remember is when using this approach we need to have all of our async code return promises, as this approach won’t work with async callbacks that aren’t promisified.
I’ve released an npm package that should make this snippet easier for you to use. You can find it here.