REST API Versioning Strategies
Today microservices are a hot trend for developing cloud-native applications. API versioning helps to iterate faster when the needed changes are identified.
Join the DZone community and get the full member experience.
Join For FreeToday microservices are a hot trend for developing cloud-native applications with hosts of benefits including, improved scalability, resilient applications, technology-agnostic, faster time to market, greater business agility with CICD support.
One of the key building blocks of microservice-based architecture is the API design and contract. With API, it is important to have versioning that enables clients to use the existing REST API continuously and migrate their applications to the new API whenever they are ready.
When to Version?
Versioning helps us to navigate the breaking changes in API like changes made to the request format by introducing mandatory parameters, changes made to the response data either due to format change or structure of the response message, or deprecation of the API to provide the enhanced functionalities.
What Are the Different Ways We Can Versionize the API?
There are four different ways to version the REST API.
- Versioning through URI Path
- Versioning through Query Parameters
- Versioning through Custom Headers
- Versioning through content negotiations
Version Through URI path
It is one of the most common and straightforward ways to version the endpoints. Version need not be always numeric, or in v[x] format, you can use any meaningful identifier like data or release numbers, which helps API producing team to incorporate new changes seamlessly.
http://api.example.com/v1
Versioning Through Query Parameters
Another option is to use the version as a query parameter like:
http://api.example.com/customers?version=v1
This approach is simple and easy to implement, we can make the latest API version as the default unless the version is specified explicitly.
Versioning Through Custom Headers
We can also implement the versioning through custom headers. This helps to avoid any fillers to the URI.
@RestController
@RequestMapping("/")
public class ProductController {
@Autowired
private ProductRepository repository;
@GetMapping(value= "products", headers = {"X-API-VERSION=v1"})
public List<Product> findAll() {
return repository.findAll();
}
@GetMapping(value= "products", headers = {"X-API-VERSION=v1"})
public List<Product> findAllV2() {
return repository.findAll();
}
}
The only downside of this approach is that it requires maintaining a header to use the version and processing of headers.
Versioning Through Content negotiations
This approach helps clients to specifically request a version with Accept Header. Service can be implemented to deliver the default representation If clients don't request a specific version.
GET /customers/1234 HTTP/1.1 Accept: application/vnd.v1+json
@RestController
@RequestMapping("/")
public class ProductController {
@Autowired
private ProductRepository repository;
// Find
@GetMapping(value= "products", headers = {"Accept=application/vnd.v1+json"})
List<Product> findAll() {
return repository.findAll();
}
@GetMapping(value= "products", headers = {"Accept=application/vnd.v2+json"})
List<Product> findAllV2() {
return repository.findAll();
}
}
With this, we have enabled 2 versions of the GET /products
endpoint with the Accept
header. When the curl request is made with v1 of the header value, the response would be according to version v1.
curl -L -X GET 'http://localhost:8080/products' \ -H 'Accept: application/vnd.v1+json' [
{
"name": "IdeaPad Slim 5 (15, AMD)"
}
]
When the curl request is made with v2 of the header value, the response would be according to version v2.
curl -L -X GET 'http://localhost:8080/products' \
-H 'Accept: application/vnd.v2+json'
[
{
"name": "IdeaPad Slim 5 (15, AMD)"
}
]
When no Accept
the header is sent, it would respond with the default version, ie v1 here:
curl -L -X GET 'http://localhost:8080/products'
[
{
"name": "IdeaPad Slim 5 (15, AMD)"
}
]
406 Not Acceptable
curl -L -X GET 'http://localhost:8080/products'
-H 'Accept: application/vnd.v3+json'
{
"timestamp": "2021-09-13T14:30:12.263+0000",
"status": 406,
"error": "Not Acceptable",
"message": "Could not find acceptable representation",
"path": "/products"
}
Note: GitHub has implemented versioning with content negotiation take a look at that in their documentation.
Summary
As API-driven architectures are evolving and more and more people are adopting, it is important to version the APIs to minimize the disruption with changes to API and to have better backward compatibility. Each of the above-mentioned versioning techniques helps with its own pros and cons.
Opinions expressed by DZone contributors are their own.
Comments