Versioning REST Web Services

Managing changes to APIs is hard. That is no surprise to anyone who has ever maintained an API of any sort. Web services, being a special case of API, are susceptible to many of the difficulties around versioning as other types of APIs. For HTTP based REST style web services the combination of resources and content negotiation can be used to mitigate most of the issues surrounding API versioning.

Let’s assume you have a REST/HTTP web service that has some account resources. Say you can make a request like this:

===>
GET /accounts/3 HTTP/1.1
Accept: application/vnd.mycompany.myapp+xml
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.mycompany.myapp+xml

<account>
  <name>Inigo Montoya</name>
</account>

First, you probably noticed that my example uses a vendor MIME media type to describe the representation. Using a more generic MIME media type like application/xml is much more common, at least in my experience. Using generic media types is perfectly legal but a bit silly. You are not really asking for any old XML document, but rather an XML document that has a quite specific schema. Aside from my idealistic rantings, using a specific media type has some strong practical benefits which are at the core of this post.

Backwards compatible changes

Often changes will need to be made to expose new behavior of the system that do not negatively impact correctly implemented clients. Say, for example, you want to start tracking email address for accounts. If the application/vnd.mycompany.myapp+xml format documentation is clear that elements that are not recognized should be ignored you can simply add a email element to the account representation.

<account>
  <name>Inigo Montoya</name>
  <email-address>mailto:prepare-to-die@youkilledmyfather.example</email-address>
</account>

Any client that was created before the addition of the email element will simply ignore it’s presence. Problem solved.

Incompatible changes

Unfortunately, not all changes can be implemented in a way that is backwards compatible. For example, a couple of months after adding email to accounts the sales team sign a deal for 1 bazillion dollars. But the new customer demands that each account be allowed to have more than one email address. After thinking for a while, you decide that the best way to expose that is by changing the account representation as follows.

<account>
  <name>Inigo Montoya</name>
  <email-addresses>
    <email-address priority='1'>mailto:prepare-to-die@youkilledmyfather.example</email-address>
    <email-address priority='2'>mailto:vengeance@youkilledmyfather.example</email-address>
  <email-address>
</account>

That, of course, will break any clients that are expecting the old format – so pretty much all of them. This is a place where we can bring content negotiation to bear. You can simply define a new media type – say application/vnd.mycompany.myapp-v2+xml – and associate new multi-email format with it. Clients can then request whichever format they want. Older clients don’t know the new media type so they get served the older single email format.

===>
GET /accounts/3 HTTP/1.1
Accept: application/vnd.mycompany.myapp+xml
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.mycompany.myapp+xml

<account>
  <name>Inigo Montoya</name>
  <email-address>mailto:prepare-to-die@youkilledmyfather.example</email-address>
</account>

Newer clients do know the new media type so they can have access to the new functionality.

===>
GET /accounts/3 HTTP/1.1
Accept: application/vnd.mycompany.myapp-v2+xml
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.mycompany.myapp-v2+xml

<account>
  <name>Inigo Montoya</name>
  <email-addresses>
    <email-address priority='1'>mailto:prepare-to-die@youkilledmyfather.example</email-address>
    <email-address priority='2'>mailto:vengeance@youkilledmyfather.example</email-address>
  <email-address>
</account>

Everyone gets what they need. Easy as pie.

Alternate approaches

The most commonly proposed approach for versioning REST/HTTP web service interfaces today seems to be to mutilate the URIs by inserting a version. For example,

http://foo.example/api/v1/accounts/3

I really hate this approach as it implies that an account in one version of the API is really a different resource than the account in a different version of the API.

It also forces clients into a nasty choice, either support multiple versions of the API simultaneously or break one of the core constrains of REST. For example, say a client exists for the v1 API that saves references (URIs that include the version indicator) to accounts. Some time later the client is updated to support the new version of the API. In this situation the The client can support both versions of the API simultaneously because all the previously stored URIs point at the old version of the API or it has to mung all the URIs it has stored to the point at the new API. Munging all the URIS breaks the HATEOAS constraint of REST and supporting multiple versions of the API is a maintenance nightmare.

Conclusion

Changes to REST/HTTP web service interfaces come it three basic flavors, changes to the properties associated with a type of resource, additions of new types of resources and deprecation of obsolete types of resources. If you are following the HATEOAS constraint of REST the approach described here can be used to safely handle all three scenarios.

This approach does lead to media types being created, but media types are cheap so we can – and should – have as many as we need. Used properly, content negotiation can be used to solve the problems related to versioning a REST/HTTP web service interface.


If you’re interested in REST/HTTP service versioning be sure not to miss the rest of the series.

Comments 6

  1. Jim Purbrick wrote:

    How does this work with read/write APIs? If I add an attribute as a non-breaking change and so don’t change the media type, old clients will continue to PUT and POST data missing the new attribute. I can fix this on the server by setting the attribute to a default value, or leaving the current value of the attribute unchanged, but does it cause problems with caches, which may replace their cached version with the partial update? Isn’t this effectively the same problem as doing partial updates with PUT?

    Posted 08 Dec 2011 at 7:34 am
  2. Peter Williams wrote:

    The important characteristic of PUTs is that they must be idempotent, not that they do not allow partial updates. It is very common for the body of PUT request to not include every property of the resource. For example, last updated timestamp fields.

    I this case the put would be idempotent. The older client neither knows nor cares about this new property and so it does not really care how it’s values are handled. Either of the ways you mentioned to handle the missing property value would be ok.

    Caches will take care of themselves. Caches don’t use the body of the PUT. They merely invalidate the previously cached representations for that resource. That means the next GET request for that resource will go to the server and it’s response will be cached. Assuming the new property is always returned the caches will keep up to date.

    Posted 08 Dec 2011 at 8:36 am
  3. Michael wrote:

    You talk about using the ACCEPT header – does the same apply to the Content-type of a POST/PUT? Tha API consists of both input AND output.

    Posted 22 Dec 2011 at 7:08 pm
  4. Peter Williams wrote:

    Michael,
    It absolutely applies to the content type of POST and PUT requests. Just as the accept header field of GET requests allows the server to give the client the particular representation it requires, the content-type header field of POST and PUT requests gives the server the information it needs to correctly interpret the data set by the client.

    Posted 27 Dec 2011 at 9:46 am
  5. Jason Heiss wrote:

    Peter,
    In your comments both here and on the “response to Jean” post you indicate that you don’t think an accept-extension like “level” or “version” is appropriate for indicating incompatible (i.e. major version) changes. However my reading of the examples in the HTTP spec and related historical documents leads me to believe that was exactly the intended purpose. All usage of the “level” extension historically seems to have been in relation to the “text/html” MIME type, and specifically to indicate major versions of the HTML spec (HTML 2, HTML 3, etc.) This page (http://www.w3.org/MarkUp/table-deployment) dated 1995 clearly shows usage of a “text/html; level=3” media type to indicate to HTML 2 only clients that they won’t understand the markup and should not attempt to do so.

    That said, this is largely an academic exercise. In the absence of specific definition it seems like media types must be treated as opaque strings, so “application/vnd.mycompany.myapp-v2+xml” and “application/vnd.mycompany.myapp+xml; level=2” both serve the same purpose. Since there seems to be consensus that the former is the better format to use I’ll be going that way, I just don’t see any reason that the later format is any less valid. And in fact using the “level” extension for minor versioning seems counter to historical precedent, but (IMHO) acceptable and harmless if used on a custom MIME time.

    Posted 02 Feb 2012 at 7:46 am
  6. Ruben wrote:

    Hi there to every one, it’s really a nice for me to pay a visit this site, it contains priceless Information.

    Posted 12 Sep 2012 at 12:11 pm

Trackbacks & Pingbacks 13

  1. From Link dump for August 31st | The Queue Incorporated on 31 Aug 2011 at 10:03 pm

    […] Peter Williams – Versioning REST Web Services – an old article but a nice alternative for handling API versioning […]

  2. From Confluence: Engineering on 05 Mar 2012 at 9:58 am

    SeeMyRadiology API…

    Introduction SeeMyRadiology uses the REST architectural style to expose integration functionality. This document describes the media types and representations used by the API. Intended Audience This document is not an introduction to REST…….

  3. From Kari's World » Blog Archive » REST in pieces on 09 Mar 2012 at 1:27 pm

    […] For API versioning, well, there is some kind of concept ideas: Versioning REST Web Services […]

  4. From How are REST APIs versioned? | Lexical Scope on 12 Mar 2012 at 11:19 am

    […] Peter Williams […]

  5. From Notes on RESTful APIs – wooptoo on 26 May 2012 at 11:41 am

    […] Versioning REST web services […]

  6. From Confluence: Mediusflow 11 Platform on 06 Jul 2012 at 5:33 am

    Ideas for summer 2012…

    Ideas for internship for Summer 2012 RESTful data…

  7. From Quora on 22 Jul 2012 at 5:30 pm

    What are some strategies for versioning APIs?…

    If you need to publish and document radical alterations to your APIs once every few years but need to I recommend explicit versioning at the root of your path. A REST example might be /api/v2/resource/123/sub/ doing this at the subdomain level might be…

  8. From Confluence: Architecture on 19 Sep 2012 at 2:19 am

    WebServiceVersioningPolicy…

    WebService Versioning Policy Links to information on WebService Versioning…

  9. From The Joys Of XML on 24 Sep 2012 at 12:18 pm

    […] is still in the pipeline. Just as there are still tons of companies that still use EDI right?I'm currently working on a project that requires posting and reading response online using XML. I'm …(yawn), have website user make selections and POST that back as plain old XML, get another response […]

  10. From Confluence: Architecture on 05 Nov 2012 at 9:40 am

    WebServiceVersioningInfo…

    Common techniques for WebService Versioning # Understand change types # Use a flexible binding approach ## Use indirection to acquire a service endpoint (Use UDDI as a versionaware service registry) ## Avoid assumptions that any aspect of a service,……

  11. From Painfully Obvious → Blog Archive → Hypermedia APIs, Part One on 21 Dec 2012 at 8:09 pm

    […] than version our API with MIME types, we used a separate X-Gowalla-API-Version header, defaulting to the most recent version if a […]

  12. From ссылки о hypermedia APIs — software simian's typewritings on 26 Dec 2012 at 4:50 am

    […] версиями API — это разные ресурсы. Он же предлагает и красивый альтернативный способ добавлять версию в API: Accept: application/vnd.mycompany.myapp-v2+xml. Концептуально это мне […]

  13. From Confluence: FrameSolutions on 02 Apr 2013 at 1:47 pm

    REST-API – Definition…

    Introduction TBW The API definition pages should b…