This article describes four common REST API versioning strategies and explains how we version the REST API at xMatters.
At xMatters, we follow the SemVer specification – we update the API major version whenever we introduce breaking changes. Internally, we update minor and patch versions whenever we add functionality and backward-compatible updates. When we release a new major version of the xMatters REST API, clients can choose to either continue using an existing major version or migrate to the new one.
REST is by far the most prominent architectural style used today to expose services to third parties over the internet. It uses the HTTP standard instead of more complex protocols like SOAP or RPC. The reason REST has been so successful is that it mimics how the web works:
- Stateless: Doesn’t retain client context between requests
- Cacheable: Relies on HTTP caching rules
- Client/Server oriented: Separates concerns between clients and servers
- Layered: Leverages a layered system and a unified interface
Four common REST API versioning strategies
One of the major challenges surrounding exposing services is handling updates to the API contract. Clients may not want to update their applications when the API changes, so a versioning strategy becomes crucial. A versioning strategy allows clients to continue using the existing REST API and migrate their applications to the newer API when they are ready.
There are four common ways to version a REST API.
1. Versioning through URI Path
One way to version a REST API is to include the version number in the URI path.
This strategy is used by xMatters as well as other DevOps teams at companies such as Facebook, Twitter, Airbnb, and others.
This solution often uses URI routing to point to a specific version of the API. Because cache keys (in this situation URIs) are changed by version, clients can easily cache resources. When a new version of the REST API is released, it is perceived as a new entry in the cache.
The internal version of the API uses the 1.2.3 format, so it looks as follows:
- Major version: The version used in the URI and denotes breaking changes to the API. Internally, a new major version implies creating a new API and the version number is used to route to the correct host.
- Minor and Patch versions: These are transparent to the client and used internally for backward-compatible updates. They are usually communicated in change logs to inform clients about a new functionality or a bug fix.
This solution has a pretty big footprint in the code base as introducing breaking changes implies branching the entire API.
2. Versioning through query parameters
Another option for versioning a REST API is to include the version number as a query parameter.
“Versioning is a crucial part of API design that gives developers the freedom to refactor their code and work on better representations for the resources of their API.”
This is a straightforward way of versioning an API from an implementation point of view. It is also easy to default to the latest version if a query parameter is not specified.
The main drawback comparing to the URI versioning is the difficulty of routing. Query parameters are in fact more difficult to use for routing requests to the proper API version.
3. Versioning through custom headers
curl -H “Accepts-version: 1.0”
REST APIs can also be versioned by providing custom headers with the version number included as an attribute.
“The main difference between this approach and the two previous ones is that it doesn’t clutter the URI with versioning information.”
The main difference between this approach and the two previous ones is that it doesn’t clutter the URI with versioning information.
4. Versioning through content negotiation
curl -H “Accept: application/vnd.xm.device+json; version=1” http://www.example.com/api/products
The last strategy we are addressing is versioning through content negotiation.
This approach allows us to version a single resource representation instead of versioning the entire API which gives us a more granular control over versioning. It also creates a smaller footprint in the code base as we don’t have to fork the entire application when creating a new version. Another advantage of this approach is that it doesn’t require implementing URI routing rules introduced by versioning through the URI path.
One of the drawbacks of this approach is that it is less accessible than URI-versioned APIs: Requiring HTTP headers with media types makes it more difficult to test and explore the API using a browser.
Versioning is a crucial part of API design. It gives developers the ability to improve their API without breaking the client’s applications when new updates are rolled out.
Content negotiation is a more granular approach because it versions resource representations instead of versioning the entire API, but it also comes with a high implementation cost for both clients and developers. More often than not, content negotiation needs to be implemented from scratch as there are few libraries that offer that out of the box. The other approaches are easier to implement and use but limit the developer’s ability to refactor due to the high cost of introducing breaking changes to the API contract.
Do you have a different strategy for API versioning? Do you have experience with these strategies? Let us know on our socials! To try xMatters for yourself, race to xMatters Free, and you can use it free forever.