A Guide to API Versioning in Spring Boot
Why API Versioning is needed
Consider we have a API xyz and its being used by many third party vendors, we are adding features , fixing bugs etc. regularly thinking third party vendors like it ,sometimes this is not the case , according to their requirement they want to be on older features of the api (they might not need those features for their work that we have newly introduced) so in such scenarios we should do API Versioning (The talked case is only a scenario ,we can have more reasons to do API Versioning).
Because API versioning is costly for both API consumers and developers, it’s considered a best practice to version your API only in the event of a breaking change. A breaking change is any change to your API that may cause client applications to fail. Breaking changes require the API consumer to rewrite their software to avoid problems.
If we have non-breaking changes we should avoid doing API versioning.
How to do API Versioning
We’ll look at five common versioning techniques: URL path versioning, Request parameter versioning, Request header versioning, Media type (Accept header) versioning and Versioning using content negotiation.
1. URL Path Versioning
URL path versioning involves including the version number directly in the URL path of the API endpoints. This is a common approach and makes the version explicit in the URL.
Implementation Steps:
- Define Controller Packages: Organize your controllers into different packages based on versions. For example, create packages like
v1
,v2
, etc. - Create Controller Classes: Inside each version package, create controller classes with the desired API endpoints.
- Define Request Mappings: Use the
@RequestMapping
or@GetMapping
,@PostMapping
, etc. annotations to define your API endpoints. Include the version number in the URL path.
@RestController
@RequestMapping("/v1/users")
public class UserControllerV1 {
// Endpoint implementations...
}
@RestController
@RequestMapping("/v2/users")
public class UserControllerV2 {
// Endpoint implementations...
}
2. Request Parameter Versioning
In this approach, the version number is included as a query parameter in the URL.
Implementation Steps:
- Create Controller Class: Create a single controller class for the API endpoints and use a query parameter to specify the version.
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping(params = "version=1")
public ResponseEntity<String> getUserV1() {
// Endpoint implementation for version 1
}
@GetMapping(params = "version=2")
public ResponseEntity<String> getUserV2() {
// Endpoint implementation for version 2
}
}
3. Request Header Versioning
In this approach, the version number is included as a custom header in the HTTP request.
Implementation Steps:
- Create Controller Class: Similar to the previous approach, create a single controller class for the API endpoints.
- Use Request Header: Use the
@RequestMapping
annotation with theheaders
attribute to specify the required custom header.
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping(headers = "API-Version=1")
public ResponseEntity<String> getUserV1() {
// Endpoint implementation for version 1
}
@GetMapping(headers = "API-Version=2")
public ResponseEntity<String> getUserV2() {
// Endpoint implementation for version 2
}
}
4. Media Type (Accept Header) Versioning
In this approach, the version is specified in the Accept
header of the HTTP request.
Implementation Steps:
- Create Controller Class: Create a single controller class for the API endpoints.
- Use Media Type: Use the
produces
attribute of the@RequestMapping
annotation to specify different media types for different versions.
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping(produces = "application/vnd.company.app-v1+json")
public ResponseEntity<String> getUserV1() {
// Endpoint implementation for version 1
}
@GetMapping(produces = "application/vnd.company.app-v2+json")
public ResponseEntity<String> getUserV2() {
// Endpoint implementation for version 2
}
}
5. Versioning using Content Negotiation
Similar to media type versioning, content negotiation uses the Accept
header to determine the version.
Implementation Steps:
- Create Controller Class: Create a single controller class for the API endpoints.
- Configure Content Negotiation: Configure content negotiation in your Spring Boot configuration.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("v1", MediaType.valueOf("application/vnd.company.app-v1+json"));
configurer.mediaType("v2", MediaType.valueOf("application/vnd.company.app-v2+json"));
}
}
- Use Content Negotiation in Controller: Use the configured media types in the controller methods.
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping(produces = "v1")
public ResponseEntity<String> getUserV1() {
// Endpoint implementation for version 1
}
@GetMapping(produces = "v2")
public ResponseEntity<String> getUserV2() {
// Endpoint implementation for version 2
}
}