Categories
Express Node.js

5 best practices for building a modern API with Express

4 min read

Express is relatively simple to get up and running. It also provides plenty of flexibility, making it a popular choice when choosing a framework to build an API with. Because of this, there are many tutorials and courses available which will teach you how to build an API with Express, however they can leave you uncertain about what best practices you should be considering for things like validation and error responses.

The following best practices will help you when designing a new API with Express, as well as with improving an existing one. Even better, these best practices will set you up with some features that other newer frameworks provide, but which aren’t included out-of-the-box with Express. Let’s get stuck in!

1. Enable the full use of async and await

Express technically works fine if you use async and await in your middleware or route handlers, however you must write extra code to catch the error that gets thrown when an awaited promise rejects, and then you must call the next() function with that error. If you don’t the request will likely hang and no response will be sent to the client. This can can get quite messy and is easy to forget about.

The express-async-errors package allows you to write modern JavaScript with async and await, without needing to worry about catching and handling every potential error, as it does all of this for you automatically. It doesn’t need any configuration: just require it after express and you’re good to go!

2. Validate request data with JSON Schema

You should never just trust data which is is sent in a request to your API as it could easily contain mistakes, or worse it might contain malicious data which has been crafted by an attacker in an attempt to crash your application or steal data. This means that you should always validate any data which is sent to your API before you do anything else with it e.g. store it in a database.

JSON Schema is an established standard which you can use to describe the format that you expect data to be in – a "schema". If data fails validation against a schema you will be provided with detailed error messages which you can then pass on to the client in your API response. JSON Schema is very powerful, allowing you to create schemas which validate complex data structures, however a schema could be as simple as checking that a piece of data is a string, with a schema like this:

{ "type": "string" }

The express-json-validator-middleware package brings support for JSON Schema into your application and enables you to validate requests to your API against any schemas that you define and configure it to use. The ‘Example Express app‘ in the package documentation gives a really good example of how you can use it for your API.

3. Use an existing format for error responses

It’s very tempting when building an API to invent your own format for error responses, but HTTP response status codes are a great starting point as they each communicate a specific error state. If you need to provide additional context beyond this about why the error occurred – and perhaps what can be done to resolve the issue, in the case of a client error – it’s worth considering applying the application/problem+json specification. It’s a proposed specification for an error response format from HTTP APIs, meaning that you don’t need to come up with your own. Here’s an example response using this format:

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en

{
    "type": "https://example.net/validation-error",
    "title": "Your request parameters didn't validate.",
    "invalid-params": [
        {
            "name": "age",
            "reason": "must be a positive integer"
        },
        {
            "name": "color",
            "reason": "must be 'green', 'red' or 'blue'"
        }
    ]
}

For more details on sending error responses in this format you can take a look at the draft specification: RFC7807 – Problem Details for HTTP APIs.

4. Send CORS response headers so web pages can call your API

If you want front end JavaScript on a web page to be able to make requests to your API, you’ll typically need your API to send CORS (Cross-Origin Resource Sharing) headers in the response. These headers tell web browsers whether it is ok or not for the web page making the request to access the contents of the API response.

You can add the cors middleware package to your application to help you send the correct CORS response headers from your API endpoints. By default, the headers it sends will allow any web page to make requests to your API, so make sure you check out the configuration options, and at the very least set the origin option so that you are restricting which web pages are able to call your API (unless you’re running an API for public use, in which case this won’t be an issue).

5. Separate your concerns

This is an important design principle to apply when building any type of software: split up your code into distinct modules, with single purposes and well defined interfaces. When building an API with Express it’s easy to make the mistake of mixing multiple concerns in a single module e.g. Express app configuration, route definitions, route handler middleware, database calls. Just because you can do this definitely doesn’t mean that you should! Building an application in this way will make your code much harder to test, debug, maintain and extend in the future.

If you want to see what a clear separation of concerns can look like for an Express based API, sign up to my mailing list and I’ll send you a link to the code for an example application which demonstrates this.

It is certainly possible to refactor your application later on and separate the concerns, but if you can consider how you want to do this early on, while planning and designing your API, it will result in a much more stable foundation for future development.

8 replies on “5 best practices for building a modern API with Express”

I’ve honestly never validated request data with JSON schema but it sounds like an important thing to do and it looks like the express middleware makes it quite easy to do. ALSO I’m liking the sound of the error response specifications. Great blog post!

Thanks, Johan.

I’ve not worked with OpenAPI specifications before, but I did see that GitHub released one for their REST API recently and I really like the approach. That middleware looks like a really good way of validating requests _and_ responses, which is something that the newer Fastify framework offers. Thanks for sharing this!

Great article, I was wondering how to validate input form before sending it to mongo without using mongoose and the JSON schema can do the job perfectly, thanks! I’m not sure if this is the place to go for advice, but it looks like you have some experience with express and I have a recurring error in my express app and no one on stackoverflow seems to be able to answer it. I am sharing the link in case you know the solution to this problem, and apologize in advance if this is not the appropriate place : https://stackoverflow.com/questions/63781229/prevent-multiple-callback-error-when-client-send-multiple-requests

Thanks, David, I’m glad you found this article useful. I’ve taken a quick look at your post, but I can’t immediately see what’s causing the issue – I’ll take a more detailed look over the weekend and let you know if I spot anything then.

In the meantime, if anyone else is able to help David out, please take a look at the link he’s posted above.

Leave a Reply

Your email address will not be published. Required fields are marked *