How to make your REST APIs backward-compatible

Take advantage of these best practices to ensure that changes to your API don’t break compatibility with current clients

How to make your REST APIs backward compatible

Representational State Transfer, commonly known as REST, is an architectural style—a set of constraints used to implement stateless services that run on HTTP. A RESTful API is one that conforms to the REST constraints. You can build RESTful APIs using many different programming languages.

Maintaining backward compatibility between different releases of your API is of utmost importance in ensuring that your API will remain compatible with all of the clients that consume it. This article presents a discussion of how you can maintain backward compatibility in your RESTful APIs.

API compatibility example

Assume that you have an API in production that is being consumed by different clients. Now if you want to add more functionality to the API, you should ensure that the clients that use the old API will be able to use either the new API or the old one. In other words, you should ensure that the existing functionality of the API will remain intact while the new functionality is added.

An API is backward compatible if a client (a program written to consume the API) that can work with one version of the API can work the same way with future versions of the API. In other words, an API is backward compatible between releases if the clients should are able to work with a new version of the API seamlessly.

Let’s understand this with an example. Assume that you have an API method named GetOrders as shown in the code snippet below.

 public IActionResult GetOrders(int customerId, int orderId = 0)
   var result = _orderService.GetOrdersForCustomer(
                 customerId, orderId);
   return Ok(result);

The GetOrders action method accepts a customer ID and an order ID as parameters. Note that the second parameter, orderID, is optional. The GetOrdersForCustomer private method is given below.

private List<Order> GetOrdersForCustomer(int customerId, int orderId)
   //Write code here to return one or more order records

The GetOrdersForCustomer method returns all orders of a customer if the orderId passed to it as a parameter is 0. If the orderId is non-zero, it returns one order pertaining to the customer identified by the customerId passed as an argument.

Since the second parameter of the GetOrders action method is optional, you can just pass the customerId. Now, if you change the second parameter of the action method GetOrders to make it mandatory, the old clients of the API won’t be able to use the API anymore.  

 public IActionResult GetOrders(int customerId, int orderId)
   var result = _orderService.GetOrdersForCustomer
                 (customerId, orderId);
   return Ok(result);

And, that’s exactly how you can break the compatibility of your API! The section that follows discusses the best practices that can be adopted to make your API backward compatible.

API compatibility tips

Now that we know what the problem is all about, how do we design our APIs the recommended way? How do we ensure that our RESTful API is backward compatible? This section lists some of the best practices that can be followed in this regard. 

Make sure that the unit tests pass

You should have tests written that will verify if the functionality is intact with a new release of the API. The tests should be written is such a way that they should fail if there are any backward compatibility problems. Ideally you should have a test suite for API testing that will fail and alert when there are issues with backward compatibility of the API. You could also have an automated test suite plugged into the CI/CD pipeline that checks for backward compatibility and alerts when there is a violation.

Never change the behavior of HTTP response codes

You should never change the behavior of HTTP response codes in your API. If your API returns 500 when it fails to connect to the database, you shouldn’t change it to 200. Similarly, if you are returning HTTP 404 when an exception occurs, and your clients are using this and the response object to identify what went wrong, changing this API method to return HTTP 200 will break the backward compatibility altogether.

Never change parameters

Avoid creating a new version of the API when you make only minor changes, as it might be overkill. A better approach is to add parameters to your API methods to incorporate the new behavior. By the same token, you shouldn’t remove parameters from an API method or change a parameter from optional to mandatory (or vice-versa), as these changes will break the API and old clients or consumers of the API will no longer be able to work with the API.

Version your API

Versioning of APIs is a good practice. Versioning helps make your API more flexible and adaptable to changes while keeping the functionality intact. It also helps you manage changes to the code better and more easily revert back to old code if the new code causes problems. You should have a different version of your RESTful API with each major release.

Although REST doesn’t provide any specific guidance on API versioning, you can version your API in three different ways: specifying the version information in the URI, storing the version information in the custom request header, and adding the versioning information to the HTTP Accept header. Although versioning can help you maintain your API, you should avoid trying to maintain many versions of the API to mark frequent releases. This will quickly become cumbersome and counterproductive. 

Other API best practices

You should never change the root URL of an API or modify the existing query string parameters. Any additional information should be added as an optional parameter to an API method. You should also ensure that optional or mandatory elements that are passed to an API or are returned from an API are never deleted.

Changes to a RESTful API are inevitable. However, unless you adhere to the best practices and ensure that the API is backward compatible, your changes can break the API’s compatibility with existing clients. An API that is in production and is being consumed by multiple clients should always be backward compatible between releases.

Copyright © 2019 IDG Communications, Inc.