50

I did my search first in stackoverflow & I was not able to find out any answers related to my question. All I can find was questions related to REST uri design.

My question in on the backend side. Suppose we have two different version of REST uri's

http://api.abc.com/rest/v1/products

http://api.abc.com/rest/v2/products

What is the best approach to follow on the backend side (server side code) for proper routing, manageability & reuse of the existing classes across these two set of api's based on version?

I have thought of approach to define resource classes with different @Path annotations for e.g. have a package for v1 & v2 separately & in ProductsResource class of that package, define

    package com.abc.api.rest.v1.products;
    @Path("/rest/v1/products")
    public class ProductsResource {...}

    package com.abc.api.rest.v2.products;
    @Path("/rest/v2/products")
    public class ProductsResource {...}

& then have the implementation logic based on the versions. The problems with this approach is when we are only changing one particular resource api from the set of api's, we have to copy other classes to the v2 package also. Can we avoid it?

How about to write a custom annotation say @Version & have values of the versions it supports? Now whether it is v1 or v2, both request will go to same resource class.

Say for e.g.

    package com.abc.api.rest.products;
    @Path("/rest/{version: [0-9]+}/products")
    @Version(1,2)
    public class ProductsResource {...}

UPDATE:

There was a API versioning suggestion by Jarrod to handle version in headers. That's also one way to do it however, I am looking forward for best practices to use when we are following URI based versioning

Deepesh M
  • 833
  • 1
  • 10
  • 18
  • 4
    best practices is to **not** put api version information in the URL –  Feb 18 '13 at 11:33
  • 9
    This was a great question, I am very surprised at the lack of responses. There are hundreds of people arguing for and against URI versioning but all the major sites do it because it is explicit and easy for clients to use. @Deepesh M - what solution did you use in the end? – arcseldon Feb 17 '14 at 12:20
  • 4
    just because lots of people do things incorrectly doesn't make it a good idea! it just means lots of people are doing it incorrectly. –  Jun 05 '14 at 02:24
  • 1
    I think having the version at the URL is against the REST concepts, As from REST perspective "v1/users" means that you are trying to get "v1 users". A good solution may be having it at the request header. – Osama Khalifa Jan 24 '17 at 14:15
  • 1
    This is a religious question, and 'best practice' in your case is likely 'what works best for you, in co-operation with the users of your API'. This question https://stackoverflow.com/questions/389169/best-practices-for-api-versioning?rq=1 outlines some fine strategies. – Morten Nørgaard Aug 07 '18 at 08:52

3 Answers3

18

The problem with putting it in the URL is that the URL is supposed to represent a resource by location. An API Version is not a location and it not part of the identifier of the resource.

Sticking /v2/ in the URL breaks all existing links that came before.

There is one correct way to specify API versioning:

Put it in the mime-type for the Accept: header that you want. Something like Accept: application/myapp.2.0.1+json

Chain of Responsiblity pattern goes well here especially if there will be significant number of API versions that are different enough to have to have their own handler, that way methods don't get out of hand.

  • 5
    Tacking on to this - in a RESTful application, your URL should represent a resource. The resource probably isn't 'versioned'; it's the same regardless of access style. If the resource _is_ versioned, it should be represented within the resource, not in its identifier. – cmonkey Feb 14 '13 at 19:11
  • @cmonkey, can you give an example? – Deepesh M Feb 15 '13 at 17:46
  • 5
    Example: my user account is a resource on SO. The url is: http://stackoverflow.com/users/322722/cmonkey. When/If SO changes features, apis, or adds data to my user account, the account is still the same resource. It would not suddenly become http://.../cmonkey_v2. Otherwise, linked content (like this comment) will no longer work. – cmonkey Feb 15 '13 at 18:20
  • 1
    Okay. I presume you are saying the resource stays the same but version should be there in header to have a differentiation between SO before & after changes. – Deepesh M Feb 16 '13 at 11:57
  • Okay, I am inclining towards having the version at the URI level. Reason being in URI is, it is noticeable & easy to handle by the api consumers. However, it was a good suggestion in case if anybody want to achieve the versioning using headers. I will edit my question to make it for uri specific versioning. – Deepesh M Feb 18 '13 at 03:53
  • 1
    @DeepeshM you are going to regret it I know from experience! –  Feb 18 '13 at 05:24
  • If the structure of the URIs change, the URI needs to have the version. stackoverflow.com/v1/users/322722, stackoverflow.com/v1/users/322722/questions, stackoverflow.com/v1/users/322722/answers, stackoverflow.com/v2/accounts/322722, stackoverflow.com/v2/accounts/322722/gaps, stackoverflow.com/v2/accounts/322722/rants, – Alex Apr 20 '15 at 14:33
  • 3
    In a true RESTful environment it doesn't matter where the versioning construct is kept or how the URI is designed. A RESTful client has no information regarding the service except for its starting URI. Everything else is learned through request and responses. If a versioning update occurs the client is still requesting the same starting-URI as it may have used before. The server, however, is now returning something different. If the content-type remained the same and only the URI changed slightly, the client might not even notice a big difference. – Roman Vottner Aug 16 '16 at 23:05
  • 1
    If the content-type changes though, the client might not be aware of what the response is all about and therefore might not be able to interpret the result and learn further actions (HATEOAS) unless it is able to find the content-type specification and is further able to interpret it. – Roman Vottner Aug 16 '16 at 23:05
  • 1
    @RomanVottner this can be handled by letting the client specify the types it understands in a Accept header in the request. – Kwebble Oct 19 '16 at 19:34
13

This blog post has an example of what is considered the by some to be the correct approach, i.e. not having the version in the URI: http://codebias.blogspot.ca/2014/03/versioning-rest-apis-with-custom-accept.html

In short, it leverages JAX-RS @Consume annotation to associate the request for a particular version to a specific implementation, like:

@Consumes({"application/vnd.blog.v1+xml", "application/vnd.blog.v1+json"})
David Dossot
  • 33,403
  • 4
  • 38
  • 72
  • 4
    Who defined what is considered the correct path? REST is an architectural style and no protocol! Unless you violate HTTP or do not adhere to the [architectural constraints](https://en.wikipedia.org/wiki/Representational_state_transfer#Architectural_constraints) there is no right or wrong, maybe some best practices though some self-claimed REST services aren't RESTful in any way therefore certain statements should be treated with special care. – Roman Vottner Aug 16 '16 at 23:11
  • You're right, I've toned down and added "by some to be" to emphasizes that not everyone agrees. – David Dossot Aug 17 '16 at 15:19
2

I was just wondering why not have a subclass of ProductService called

@Path(/v2/ProductService)
ProductServiceV2 extends ProductService {


}


@Path(/v1/ProductService)
 class ProductService{


}

and only override whatever is changed in v2. Everything unchanged will work the same as in v1/ProductService.

This defintely leads to more # of classes but is one easier way of coding for only whatever is changing in the new version of api and reverting to the old version without duplicating code.

Chetya
  • 1,267
  • 1
  • 17
  • 31
  • That was my initial thought also :) I recalled how EclipseLink implements support of JPA dialects in a similar way (Oracle11Platform extends Oracle10Platform, etc.). But then, I figured it's going to be very messy with REST. Just imagine having to extend all of your classes version after version. I like the approach with Header parameters. – jFrenetic Jul 13 '15 at 21:50
  • 6
    This is so short sighted it is not even funny. You are going to have to maintain `30` different methods or classes for every `URL` in your application when you get to `v30`? Does not scale. –  Nov 06 '15 at 15:14
  • If the changes are minor, I'm not sure why you couldn't do this. You'd only override the methods that have changed. If you are adding new methods and deprecating old ones, then couldn't you can choose a different pattern at that point? – gdbj Mar 01 '19 at 18:54
  • We are also thinking about the same approach of extending class. We are using resteasy + swagger. What kind of other apporoches are available ? – ShraddhaJ Feb 18 '20 at 09:28