DEV Community

Bernhard Häussermann
Bernhard Häussermann

Posted on • Updated on

How to implement async operations in your Express REST API

This article is a continuation of a series about how to build a REST API in Node.js. In the first article of this series, we built a rudimentary REST API for managing a list of employees. However, instead of using a real database, the EmployeesService simply maintained the list of employees in memory. Had it implemented actual calls to a database, the methods would likely have been asynchronous, returning promises rather than performing the work synchronously. In this post we will discover how to properly add support for async functions.

Let's change the EmployeesService.get() method to be asynchronous and see what happens. By adding the following await statement we simulate the database query taking 1 second before it returns the results:

Now, if we call GET localhost:3000/employees/1 the response we get is the empty object {}. Keep in mind that in JavaScript an async method effectively returns a Promise that resolves when the method completes. It appears that in employees-controller.js where we call res.json()...

Express just stringified the Promise object passed into res.json() rather than awaiting it and using the resolved value. We need to pass the resolved value into res.json() instead:

Testing the call using our REST client, this seems to work. However, when we request an employee that doesn't exist, something weird happens. The request hangs and eventually times out.

This is because the request-handler function passed into the get() method above is now async. The get() method handles the case where the passed function throws an error by making the request respond with an error code. Now that the method is async, what happens instead is that the function returns successfully returning a Promise which eventually gets rejected, and get() doesn't handle this as expected.

We can make this route handle errors correctly by using the third next parameter passed into the request-handler function like so:

Running the request for an employee that doesn't exist now returns the expected response.

Suppose now that all of our request-handler functions were async (and in a real-world application, most of them will be). We would have to wrap every operation's code into a try-catch block, and pass the error into the next function to prevent a request hanging forever should an error occur. This is far from ideal. Thankfully, the express-async-handler provides a much more convenient way of dealing with errors.

First, use npm to add the package as a run-time dependency:

npm install --save express-async-handler
Enter fullscreen mode Exit fullscreen mode

And use it in EmployeesController like so:

Of course, if we had not had to deal with the particular case of the 404, then the try-catch block would not have been needed since the asyncHandler() deals with any errors that might happen.

As long as we remember to pass each async operation's function into asyncHandler(), we can rest assured that whenever an error occurs within the operation, it will still return the correct error code and message and not just hang until it times out.

Future posts will show how we can add additional middleware such as for adding request / response validation and Swagger documentation thanks to Node.js packages available for this.

The code for the API developed in this series of articles is available on GitHub here.

Discussion (0)