20

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!

user3237736
  • 845
  • 1
  • 9
  • 24
  • Do you really need a new version? Would not an extension of the current one be enough? Then afterwards you can deprecate piecemeal bits as desired. – Perdi Estaquel Oct 23 '18 at 22:48
  • Agree with comment above. You are going to fast. The safe approach will be to build a hybrid v1/v2, and slowly deprecate v1 stuff until the v1 and v2 can be separated without risk to the live application. – Lee Irvine Oct 23 '18 at 22:53

5 Answers5

2

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 application (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 codebase which require refactoring, though making the whole process not a quick one. Besides, I believe the closer changes approach the core of the application, the more difficult parallel versioning may be, but we 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amega
  • 128
  • 6
  • Did you give the [Abstract Factory pattern](https://en.wikipedia.org/wiki/Abstract_factory_pattern) a thought yet? – Gijs Sep 13 '21 at 13:14
  • @JavaJigs that's actually it. At least, this pattern is one of the approaches which implements the described idea. – Amega Sep 14 '21 at 16:06
1

Hope you have seen

  1. API design ensuring backward compatibility
  2. http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/32713.pdf

Having two versions of API in the same application is quiet enough

api.mysite.com/[version1]/api/url

api.mysite.com/[version2]/api/url

Not sure why you need to build and deploy v1 and v2 as separate applications? Unless you are planning for a Zero-Down-time rolling upgrade in production

Sankarganesh Eswaran
  • 10,076
  • 3
  • 23
  • 24
  • 1
    REST is different in terms of versioning than a (typical) framework API by a client not interested in the URI itself but in the content it receives and the hint about which representation the content was created for (media-type). For RPC-like APIs, however, I admit that such a design would be benefitial, though REST is just the opposite, at least it should be though ppl are good in confusing REST for anything related to HTTP and sticking to ther RPC-like behaviors ... – Roman Vottner Oct 24 '18 at 12:27
  • 6
    Sankaraganesh, the question is not how to provide a /v2 endpoint, but how to implement a mix of v1 and v2 services without redundancy and inconsistency in my code - my question is geared towards the actual, specific implementation technique, and whether there are frameworks or design patterns for this – user3237736 Oct 24 '18 at 17:12
  • @user3237736 In that case, can you edit and remove the following from your question? // 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// – Sankarganesh Eswaran Oct 25 '18 at 14:08
  • 4
    @SankarganeshEswaran no i can't remove it, it's an important part of the question. i think you still misunderstand. I need v1 and v2 API, but don't want v1 and v2 application. what you suggested is just providing different version to the client through different paths. but that is not the challenge here. – user3237736 Oct 26 '18 at 16:02
1

I like to bring up the following strategies into the discussion, and 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.

See BranchByAbstraction.

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.

See Feature Toggles (aka Feature Flags)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jschnasse
  • 8,526
  • 6
  • 32
  • 72
0

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.

Eric Green
  • 1,151
  • 9
  • 17
  • 1
    Unfortunately I cannot just forget about v1. As long as our clients still use v1, i will have to fix critical bugs there. And our clients are guaranteed by contract to have a working v1 for about 6 months after release of a v2 before we can shut down v1 and force them to v2. It is because of this ongoing maintenance that I would like to avoid having different projects/branches for v1 and v2, as I would have to maintain both. – user3237736 Oct 23 '18 at 23:04
-5

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

Roman Vottner
  • 12,213
  • 5
  • 46
  • 63
  • 2
    as i cannot edit my comment, let me post a new one which i think makes more clear what my main question here is: I don't think REST, HATEOS etc has much to do with my question, so maybe I should have just spoken of a "web service". The main question is what specific implementation techniques there are to mix v1 and v2 API in one code base, where some code must be shared acrros v1 and v2, and some is different. – user3237736 Oct 24 '18 at 17:16
  • @user3237736 `so maybe I should have just spoken of a "web service"`, if you are concerned about the exchange of data than my answer still applies as the data exchanged is the important part not how they are implemented in the back. For a pure implementation perspective general SOLID principles apply. I.e. you've mentioned that your API contains stuff that does not change. Separations of concerns would recommend to split those into own little units then. As such, the question at hand should then be rephrased into a more generic "How to structure code to support software evolution" – Roman Vottner Jul 06 '21 at 09:56