REST API: Multiple versions, single application?

Viewed 13037

I am working on a REST API where I will have to introduce some breaking changes soon, so a v2 is needed. We still need to support v1 for a couple of months though in parallel, to give our clients time to switch over to the new API whenever they are ready. Our API is offered via shared cloud, and all our clients share the same system backend, especially a single, shared database.

I have found a lot of articles about REST API versioning, but they were all more from a client's point of view, or high-level design point of view. That's not really my concern, our API already has a versioning in the URI, so offering services with a /v2 base path won't be aproblem.

However I am asking myself how I am actually going to implement this, and I haven't really found good articles on that. I don't really want to branch off a v2 of my project and then build and deploy v1 and v2 as separate applications, because then I would have maintenance, bugfixing, configuration changes etc in two applications, which is double work and carries the usual dangers of redundancy (i.e.: possible inconsistencies between the versions). Also, the v2 of course is not different in every service, so most of the code would still be the same.

Is there any best practices on how to technically implement a REST API in a single application that provides multiple versions to the outside, and where some code is shared (i.e.: v2/someService would internally redirect to v1/someService), and just the actual differences are coded in new services? Maybe there's even frameworks that help with designing this? The app is coded in Java with Spring MVC if that's helpful.

I'm thankful for any tips or resources on how to tackle this. Thanks!

5 Answers

I like to bring up the following strategies into the discussion, both are strategies in Continuous Delivery.

Branch Abstraction

The basic idea is to place an abstract layer in between the clients and your current implementation. Then introduce a second implementation behind the abstract layer. This gives you the opportunity to progress within your normal code base but support new features for your next version API instantly.

https://martinfowler.com/bliki/BranchByAbstraction.html

Feature Toggles

Add features to your code base without making them visible to your customers. This allows you stay on your main development branch even if things are not ready for end users yet.

https://martinfowler.com/articles/feature-toggles.html

If I were faced with the situation you speak of, I would first try to keep my new version (v2) backward compatible with my first version (v1). If that were the case, you could just add functionality and update your API documentation, keeping only one active code base. I would think you could even add things to the response payload as long as the data coming back would not break anyone's code - sort of like adding fields to an existing database schema.

If v2 was not backward compatible with v1, you could possibly move v1 to another server and notify your users that it is being placed there for a stated, limited period to give them time to make code changes necessary to switch to v2, but also notify them that this version is no longer being updated and if they have issues, they will need to switch to the new version. Hence, v2 is the HEAD version of your code base with no other branches under active development.

I hope this helps and offers something you didn't already think of.

I'm also now facing such task, and still having no useful answers. Though I believe having separate v1 and v2 instances in parallel can still be at least a fallback-solution, I'm currently thinking about a scheme for a single application, which will heavily use the benefits of dependency injection in the application. So, basically idea is to configure your IoC container accordingly to a received request, so that each and every service would receive a needed version of its dependencies. This can theoretically be a good solution, but it requires an already near to ideal architecture of your app (which is often not the case) where all the concerns are separated etc. As SOLID as possible, in other words. At least with this approach you'll be able to quickly identify all the components of your code-base which require refactoring, though making the whole process not a quick one. Besides, I believe the closer changes approach the core of the app, the more difficult parallel versioning may be, but will see.

I should point out once more, that for me it's still just an idea which I'm going to research further specifically for my project, so I'm not sure how easy or problematic it will be in fact.

The v1/v2 dilemma is a strong hint that you actually do not have a REST API to start with. Peers in a REST architecture exchange more or less standardized content by clients requesting representations of media-types they understand. This technique is called content-type negotiation. Of course, a badly written server may ignore proposed media-types and send one the client does not understand. This will, though, prevent the client from interacting with the server further. A well-behaved server therefore should attempt to serve a client request as best as it can.

According to Fielding:

A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. Any effort spent describing what methods to use on what URIs of interest should be entirely defined within the scope of the processing rules for a media type (and, in most cases, already defined by existing media types). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]
Source

A media type describes how the syntax of a payload exchanged for such a media-type looks like as well as the semantics each element in that representation has. With the help of meaningful link relation names and media types a server can teach a client on the next options available a client can make use of while progressing through its task. I.e. think of a case where a previous response contained a link relation create to the client. The client does not really know how something has to look like in order to be processable by the server, thoug on invoking the URI returned for the create link relation name the server responds with a form like representation along the line of application/vnd.xyz-form+json where this media type defines some input controls the client can use in order to generate a request representation expected by the server on the target endpoint. Similar to the Web the custom form also contains a HTTP action as well as a target URI provided by the client to send the response to and eventually also the representation preferred by the server.

Clients in a REST architecture shouldn't care about the URI, so returning an URI containing either v1 or v2 should be more or less meaningless to them. Fielding even stated that a REST API itself shouldn't be versioned at all! What is important though is that the client and server are able to understand the payload received.

Instead of versioning the URI or API, the media type describing the syntax and semantic actually need to be versioned. I.e. if you take a look at the browser based Web (the big sibling of REST) and here HTML in particular you will notice that it is designed in a way to require new version to stay backwards compatible. I.e. client and server receiving a text/html defined payload will be able to handle pure HTML (1.0) up to HTML5 content regardless which actuall syntax (maybe even a mixture) was used. Other media types, however, might not be that lenient. Here you could either make use of profiles or register a whole new media-type if you think the old and new one are completly incompatible to each other.

Either way, I hope I could shed a bit more light on REST architecture and how you might get there. I am well aware that my suggestion may not be easy to achieve, though once you got it you basically decoupled clients from your API and gave the latter one freedom to evolve while not having to fear breaking clients. There will still be a coupling but both, client and server, will couple to the media types rather than to each other. Before creating a new media-type it is probably worth looking for already existing ones

Related