A Guide to API Versioning in Spring Boot

Avinash Kumar Singh
3 min readAug 27, 2023

--

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:

  1. Define Controller Packages: Organize your controllers into different packages based on versions. For example, create packages like v1, v2, etc.
  2. Create Controller Classes: Inside each version package, create controller classes with the desired API endpoints.
  3. 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:

  1. 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:

  1. Create Controller Class: Similar to the previous approach, create a single controller class for the API endpoints.
  2. Use Request Header: Use the @RequestMapping annotation with the headers 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:

  1. Create Controller Class: Create a single controller class for the API endpoints.
  2. 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:

  1. Create Controller Class: Create a single controller class for the API endpoints.
  2. 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"));
}
}
  1. 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
}
}

--

--

Avinash Kumar Singh

Software Developer | Competitive Programmer | Backend Developer