Different ways to do versioning of REST APIs in Spring Boot

Most of our web apps communicate through HTTP / REST APIs.

These APIs can evolve over time.

And hence maintaining different versions of them can be helpful.

For example , lets say you have been hitting a REST API which returns the different products available on an ecommerce store.

The owners decide to launch a new version of the API with improved search results but don’t want to remove the previous version because multiple vendors are already using it.

In such cases, how to create different versions of your API in an efficient way without changing the API name?

One way to do this is in your code itself.

Here are four major ways to do that in Spring Boot:

  1. URL Versioning
  2. Query Parameters Versioning
  3. Headers Versioning
  4. Media Type Versioning

URL Versioning:

In this approach, the version number is included in the URI of the API.

For example,

api/v1/user and api/v2/user could represent different versions of the /user endpoint.

This approach is straightforward, but it can lead to a cluttered URI and may require changes to clients’ code to switch versions.

Example:

@RestController
@RequestMapping("/api/v1/user")
public class UserControllerV1 {
   // ...
}

@RestController
@RequestMapping("/api/v2/user")
public class UserControllerV2 {
   // ...
}

Query Parameters Versioning:

In this approach,

the version number is included as a query parameter in the API call.

For example, api/user?version=1 and api/user?version=2 could represent different versions of the /user endpoint.

This approach keeps the URI clean, but it requires clients to remember to include the version parameter in every API call.

Also the code looks more cluttered as you need to include both the version logic inside the same method:

@RestController
public class UserController {
   @GetMapping("/api/user")
   public String getUser(@RequestParam("version") int version) {
      if (version == 1) {
         // return version 1 of response
      } else if (version == 2) {
         // return version 2 of response
      } else {
         // return default version of response
      }
   }
}

Header Versioning:

In this approach, the version number is included as a custom header in the API call.

For example, X-Api-Version: 1 and X-Api-Version: 2 could represent different versions of the API.

This approach is clean and flexible, but it may require clients to modify their code to include the custom header.

Here is a sample:

@RestController
public class UserController {
   @GetMapping("/api/user")
   public String getUser(@RequestHeader("X-Api-Version") int version) {
      if (version == 1) {
         // return version 1 of response
      } else if (version == 2) {
         // return version 2 of response
      } else {
         // return default version of response
      }
   }
}

Media Type Versioning:

In this approach, the version number is included in the media type of the response.

For example, application/vnd.company.user.v1+json and application/vnd.company.user.v2+json could represent different versions of the response for the /user endpoint. This approach requires careful design of the media types and may be less familiar to developers.

Example:

@RestController
public class UserController {
   @GetMapping(value = "/api/user", produces = "application/vnd.company.user.v1+json")
   public String getUserV1() {
      // return version 1 of response
   }

   @GetMapping(value = "/api/user", produces = "application/vnd.company.user.v2+json")
   public String getUserV2() {
      // return version 2 of response
   }
}


Usually JSON Media type is denoted by :

application/json in the http response header

For your custom type ,

You need to include your custom type name “vnd.company.user.v1” and “vnd.company.user.v2” in the above examples , followed by “+” symbol and then by “json”.

This way when a http client makes a call to the above APIs , it will include this media type as part of “Accept” header value.

These are few of the major ways to do versioning of your HTTP APIs.

Despite these options available , it is better to avoid versioning as much as possible as it makes your code more cluttered and less maintainable.

Instead you can write APIs which are :

  • More flexible to change, without breaking client code
  • Backward compatible to support older logic and new ones.

Here is a deeper discussion by Mark Nottingham on this topic:

https://www.mnot.net/blog/2012/12/04/api-evolution

That’s it!


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading