JavaVersioning REST APIs

Versioning REST APIs

Developing APIs is no simple task. It gets even more complicated when you have to update or evolve them as technology and client needs change. Once you start the development of a new API, change is inevitable. Sometimes the needs of the API can change in so many ways that the original version will no longer be enough, and you find yourself needing to rewrite the entire thing.

Evolution is the law, and you can’t skip it. Therefore, it’s better to prepare yourself and figure out the best way possible to deal with anticipated changes before that time has come.

Understanding API Contracts

It’s important to discuss first what a contract is and what they are used for. You have probably heard this term already; maybe while developing some new API endpoints, the FE developer suddenly asked for the contract. To clarify, the contract is the recipe towards which everyone will be able to understand the developer’s API.

There is some literature, such as the one from Roy Fielding, that suggests that a programmer’s APIs should be hypertext-driven. What does that mean exactly, though?

In short, it means that APIs should be descriptive enough so a consumer developer can look at them and, based on the media types that each endpoint provides, that developer would be able to infer what to do and, especially, where to go next.

Besides that, API structures may change in so many ways that a given API property might no longer attend to the prerequisites to which it was originally designated.

Take the following JSON response body as an example:

{
   "name": "string"
}

This may be the case when you originally designed this endpoint (as well as your data models). However, if times passes by and the model changes to a more suitable composition, you’d have a slightly different API contract:

{
  "name": {
    "firstName": "string",
    "lastName": "string"
  }
}

You can’t simply change the endpoint contract because the name doesn’t represent a single string anymore but, instead, a composite object. That’d break the current clients’ calls to the API. To be able to still use the same property and the new model, we need a new version.

Exploring REST API Options

There are two major we can version REST APIs: URIs and Media Types.

URIs for REST API Versioning

URI versioning happens when you add a new piece of URI to your endpoints, and so, explicitly state the version, such as in this example:

http://myapi/v1/users.

This means that for this type of strategy, ideally, developers would always consider that an API is versionable. Even if no v2 is ever provided, the APIs should start with v1. And that’s ok; there are things that hardly ever change.

Once we finalize a specific version of an API, it means that it should be immutable. Unless you’re just introducing small changes that do not break the current established contract. This is known as a style of API design with forward compatibility in mind.

API Versioning Using Media Types

The same API example, versioned under the second strategy via media types, would be similar to this:

// Request
GET http://myapi/users
Accept: application/vnd.my_service_name.v2+json

// Response
HTTP/1.1 200 OK
Content-Type: application/vnd.my_service_name.v2+json
{
  "name": {
    "firstName": "string",
    "lastName": "string"
  }
}

The Accept header enters the stage to define the specific version the client caller needs to access. You must also change the “my_service_name” with your API name/domain name.

In the same way, the return data must obey what was provided by the Accept via Content-Type header.

As per a possible implementation, it’ll depend on the language and frameworks of choice. However, they usually match in style and semantics.

You have two other options in terms of keeping separate copies of your code, as well as packages. If you’re working with Java/Spring, for example, you’d have something similar to this:

package com.mycompany.controller;

@RestController
public class UserController {

   @GetMapping(value = "/users")
   public User getUsers() {
   }
}

The example above assumes some familiarity with Spring for API development. To infer versioning from the beginning, the same controller would have to be created as follows:

package com.mycompany.controller.v1;

@RestController
public class UserControllerV1 {

   @GetMapping(value = "/users", produces = "application/vnd.mycompany.app-v1+json")
   public User getUsers() {
   }
}

Conclusion

There are pros and cons to both strategies. Here, we’re presenting just an overview of what options are available to you and how you may benefit from them.

Be careful when adopting either option before your APIs start since any new changes will impact other developers, and differences there can cause inconsistencies towards the company’s style guides.

Additionally, make sure to refer to the classic piece by Roy Fielding mentioned here.

Latest Posts

Related Stories