API design · Feb 1, 2023

REST API design: best practices for returning Boolean values in responses

REST APIs are characterized as orientated around resources that belong to the business domain and operations on those resources. Each resource is modeled as a unique endpoint, and operations are performed using standard HTTP methods. This means that REST APIs follow the same semantics as how the web works, instead of each API defining its own meaning for things which developers have to learn.

REST APIs have become immensely popular amongst developers because they are highly conventional. You can make your REST API even easier to learn and use by following certain best practices when structuring your data for certain situations. One common use case is when you need to return a Boolean value (true or false) in a response.

Examples of why might want to return a Boolean value include:

  • Status API: An API that returns the current status of a particular user, e.g. whether they are active or not.
  • Availability API: An API that returns a status indicating that the service being queried is available and ready to serve traffic. Such APIs are commonly used by cloud orchestration systems such as AWS Fargate or Kubernetes.
  • Authorization API: An API that checks whether a particular user is authorized to perform an action. Such an API may exist on its own, as opposed to merely denying the action, so that a frontend can conditionally hide or show certain application features depending on the current user’s permissions.
  • Validation API: An API that receives input data from the end user and makes sure that it passes certain checks required by the application. For example, that a username is not already taken.
  • Errors: Since REST APIs rely on HTTP semantics, some error information is contained in the HTTP status code itself, e.g. 404 Not Found. Some APIs rely solely on this and only return false for an error in the response body. (Keep reading on why this should be avoided.)

All of these examples are perfectly appropriate examples of returning a Boolean value in a response. It is highly tempting to therefore literally return true or false as the entire API response.

Keep reading to learn exactly why you should not do that.

Boolean values lack context

What would you say the following response means?

false

It could mean absolutely anything — there is no other information to provide context clues.

If you knew this response came from a GET /users/{user}/active API call then you could surmise that the user is not active. But this is additional effort that the developer has to do when reading or writing code that consumes this response.

Software is read many more times than it is written. While you often need to strike a balance between conciseness and expressiveness when developing software, a bit of extra verbosity upfront can go a long way to improving the maintainability of your code in the future. APIs are a negotiation in dividing responsibilities between the vendor and the consumer. Putting in additional effort on the vendor side means your customers have less to do.

Boolean values inhibit writing reusable code

One measure of developer experience is how maintainable and robust codebases that integrate against your API are. An API that facilitates writing clean and maintainable code will provide a greater experience to developers than one where developers have to do extra work to compensate for inconsistencies in your API.

One way that developers write maintainable code is by factoring out common concerns such as networking or response parsing into a common library. This allows developers writing the parts of the application that perform business logic to not have to worry about procedures that aren't as relevant to the task at hand.

In order for response parsing to be successfully factored out into a common library, the responses themselves need to all share a common structure. When you return a plain Boolean value in a response, then that responses become inconsistent with other responses in your API that return objects. Response parsing code becomes very difficult to write in a reusable way as each endpoint may return a different structure.

Boolean values are not future proof

This is perhaps the most important reason to avoid returning plain Boolean values as responses. There is no way to change it in the future in a backwards compatible way. This means any enhancements you make to the endpoint need to be released on a new version, which your customers will have to migrate to.

Best practices for returning Boolean values in responses

We can improve on all the examples above by making two changes:

  1. Change the Boolean value to an Enumeration, that is a set of fixed string values with documented meaning.
  2. Embed the value inside a top-level JSON object.

These changes have the added effect of making your API resilient to any future changes. Additional states beyond a binary true or false can be accommodated by adding additional possible values to the Enumeration. Additional related information can also be included alongside the original value.

Let's look at how the above examples can be improved by following these practices.

Status API:

The Status API example above can be transformed like so:

{
  "status": "active"
}

Now additional status like ‘archived’ can be introduced. Also the response can now return additional data without breaking clients. For example, the date the status was last updated:

{
  "status": "active",
  "status_changed": "2023-02-01T10:00:01"
}

Availability API:

We can provide additional information in the response to explain why the service is not available.

{
  "status": "fail",
  "dependencies": {
    "database": {
      "status": "fail"
    }
  }
}

Here we're communicating that the service is not available due to a problem with the database connection.

Authorization API:

We can provide additional context about the action that was not authorized.

{
  "resource": "documents/788821d2-620b-4cf1-86a3-da5335855943",
  "action": "delete",
  "authorization": "unauthorized"
}

This let's anyone seeing this response immediately know that the action that was denied was a Delete Document action. If you were debugging an unauthorized response because you were trying to edit a document, this would immediately tell you that the wrong action was being specified ("delete" instead of "edit").

Validation API:

We can even build a more customer-centric API by taking into account the workflow the user is trying to perform and design the API around the next step in the user experience, rather than just describe the current step.

{
  "username": "apidesigner",
  "availability": "taken",
  "suggested_alternatives": ["apidesigner86", "apidesigner749", "i_am_apidesigner"]
}

Here not only are we providing additional context about the Check Username request that this response corresponds to, but also providing a path forward for the client by suggesting alternative usernames that they could use instead when the one they want is taken.

Errors

Finally error responses are the classic example where more information is usually better.

{
  "type": "https://example.com/probs/out-of-credit",
  "title": "You do not have enough credit.",
  "detail": "Your current balance is 30, but that costs 50.",
  "instance": "/account/12345/msgs/abc",
  "balance": 30,
  "accounts": ["/account/12345", "/account/67890"]
}

Here were are communicating four things that a plain Boolean value couldn't:

  1. What kind of error occurred in a machine-readable way.
  2. What kind of error occurred in a human-readable way.
  3. Details about this specific occurance of the error in a machine-readable way.
  4. Details about this specific occurance of the error in a human-readable way.

Conclusion

When it comes to API, terseness is not always better. Avoid returning plain Boolean values in responses, even where it may seem straightforword and common sense in the moment. In the very least your API will be future proof and resilient to change if your requirements change. In the best case, you will open up the API's design to support workflows that are more customer-centric.