Sunday, March 2, 2014

Versioning REST APIs with a custom Accept header in Jersey

Versioning REST APIs is a hot topic these days. In this post, I will show you how versioning with a custom Accept header works in Jersey, without going into the politics of which versioning approach is the best. I will build on the project from one of my previous posts on rest.

The idea is to use a custom media type instead of using one of the defined media types such as application/xml and application/json. The resource URIs remain unchanged, but a user can ask for different representations of a resource based on media type. Let's define a media type for the blog API as an example:

application/vnd.blog.v1+json
application/vnd.blog.v1+xml

Now, we can modify our resources to consume and produce these media types:

I was pleasantly surprised that Jersey (version 2.6) understands this media type format out of the box. It interprets +json and +xml clauses correctly and uses existing providers.

When our entities change in a non-compatible way, a new version of the API can co-exist with the first version given that that entity namespace is also changed. The newer API will have its own interface and use an updated media type:

Now the client is required to specify an accept header when using our API. We can remedy this by adding default media types to the latest version of our API. This way, clients invoking the API without an Accept header will be working with the latest version.

And that's it. The complete working maven project can be accessed from GitHub.

10 comments:

  1. how to register this new custom mimi type in server? thanks

    ReplyDelete
  2. I don't think you need to register the mime types, at least I didn't have to when using JBoss AS and Jetty.

    ReplyDelete
  3. Hi Nazar, I know this post is pretty old but, what if I don't want to modify the MIME type and instead add a header "version: 1.0" or something like that, is there an annotation in Jersey to also add a custom header so the chain of responsibility can select the java method?

    This is because there are certain enterprise proxies that actually do filter HTTP traffic by media type, and any uncommon MIME types get 401'd on the client behind the proxy

    ReplyDelete
    Replies
    1. I know this is possible in Spring MVC through a custom request mapping handler, and I think something similar exists in Jersey, try this: http://stackoverflow.com/questions/25881675/registering-a-custom-resourcemethodinvocationhandler-in-jersey

      Delete
  4. Nazar, thanks for the blog post. I wanted to know how we can maintain backward compatibility when we use this type of versioning? how will already existing clients continue to function unhindered?

    ReplyDelete
    Replies
    1. When creating new API versions, you are creating new interfaces and API classes, such that the old remain as is and work as is. You have to make sure that the domain model under the hood can server both the new and the old version.

      Delete
  5. Hi,

    If you specifying a media type in @Consume annotation then the client must send Content-Type header with same media type otherwise server would throw 415, it just can't be accept header

    ReplyDelete
  6. IMHO it could be confusing if you have 50+ versions of your api.
    O.O

    ReplyDelete
  7. ExpressTech Software Solutions is known as best custom Rest API Development Company in India, USA, has the best in the industry specialization to deliver seamless API integration and development services. +91-9806724185 or Contact@expresstechsoftwares.com

    ReplyDelete
  8. @Consumes({ MediaType.MULTIPART_FORM_DATA, "application/qorus.api.rest+json" })

    I did that, problem is that it allows without the accept header as far as it's a multipart

    ReplyDelete