Search code examples
restapiversioningatom-feed

REST API versioning when using Atom for resource collections


I know this is something that has been discussed over and over, and I have done extensive research to get where I am so far, but can't seem to get over the final hurdle.

I am designing a custom REST api for our application, and have decided that I would like to version using media types e.g. application/vnd.mycompany.resource.v2+xml. I realise the pro's and con's of this model and it seems to weigh up the most flexible.

Hence my GET would look as follows:

=== REQUEST ===>
GET /workspaces/123/contacts?firstName=Neil&accessID=789264&timestamp=1317611 HTTP/1.1
Accept: application/vnd.mycompany.contact-v2+xml

<== RESPONSE ===
HTTP/1.1 200 OK
Content-Type: application/vnd.mycompany.contact-v2+xml
<contact>
    <name>Neil Armstrong</name>
    <mobile>+61456838435</mobile>
    <email>[email protected]</email>
</contact>

The problem is that I would like to use Atom feeds and entrys to represent my resource collections. This way I can harness the searching and pagination of Atom without this infected my resources or API structure.

If I use Atom for my requests, my request structure now looks like:

=== REQUEST ===>
GET /workspaces/123/contacts HTTP/1.1
Accept: application/atom+xml; type=feed;

<== RESPONSE ===
HTTP/1.1 200 OK
Content-Type: application/atom+xml; type=feed;
<feed xmlns="http://www.w3.org/2005/Atom">
   <title>Contacts Feed</title>
   <link rel="self" href="https://api.mycompany.com/workspaces/contacts"/>
   <updated>2011-11-13T18:30:02Z</updated>
   ...
   <entry>
      <title>Neil Armstrong</title>
      ...
      <content type="application/vnd.mycompany.contact-v2+xml">
          <contact>
              <name>Neil Armstrong</name>
              <mobile>+61456838435</mobile>
              <email>[email protected]</email>
          </contact>
      </content>
   </entry>
</feed>

Using Atom to represent my collections of resources, I lose the ability to version using media types. As the media type is now hidden within the content of the Atom entry.

      <content type="application/vnd.mycompany.contact-v2+xml">

What is the best practice for determining the media type version of my resource, while still utilising the power of Atom for Resource collection management?

My thinking is that I could pass it through the ACCEPT header e.g.

Accept: application/atom+xml; type=feed; version=1.0

But then this is confusing as you are asking for version 1.0 of the Atom feed, not the resource itself...

Any help would be really appreciated!!


Solution

  • The problem is that IMHO you're misusing the media types.

    Media types give you information on the STRUCTURE of the actual payload, but not the SEMANTICS of the payload. "I know this is an XHTML page, but I don't know if it's a blog post or a item on Amazon." By being an XHTML page, you know how to get the component parts out of the payload and ask interesting questions, but interpretation of the payload is not part of the media type.

    Consider an example, paraphrased from an example of Roy Fielding, sending a 10,000 bit array as a GIF file that's 100x100 pixels. GIF, as "everyone knows" is used for sending pictures, but it's really simpler than that. It's a mechanism for sending structured binary that just-happen to most-of-the-time be images. So, in this case of using it to send a 10,000 bit array (perhaps represented as a gray scale image of 00 and FF), you get the benefit of a common decoder (GIF), GIFs built in compression, etc.

    But, in this case, it's not a picture. You can show it as a picture, but it's a meaningless picture. The classic semantic of it being used for the picture scenario is not relevant in this case. The benefit is the ubiquity of the the format.

    Another example was years ago an engineer was doing radar studies. So, he would take the 3-view drawings of aircraft you would find in books and such, and he would encode them using a tablet in to AutoCAD drawings. The DWG format was well documented, and he had code to read them. What he wanted was the coordinates and measurements from the specific aircraft.

    So, in the end he had a bunch of "meaningless" AutoCAD files with nothing but a bunch of lines in it that "made no sense". But in fact they were chock full of good information for his domain. The DWG file was the media-type, but these weren't "CAD drawings". (Can you say "spontaneous reuse"?)

    It's fine to version something via media-type, but that's only relevant if the media-type is in fact changing. ATOM, as you noted, isn't changing, or at least it's not changing under your control, and you may choose not to support the new version if/when it does change. But ATOM is not changing because how it represents its information, how that information is encoded, is not changing. The information may well change, in fact it changes all the time. Every ATOM feed is different with different information. MOST have similar semantics (blog feeds), but many do not (for example, perhaps your scenario).

    But how you will parse and get information out of the ATOM feed will not change. And that's what the media type represents. An encoding of information, not the information itself.

    So, if you want to detect versioning, then check within your payload. Inspect it. You KNOW that for V1 of your data where, for example, the invoice number is (perhaps it's at invoice/inv_no in XPATH). If the invoice is NOT there, then what do you do? You a) look some other well known place (i.e. V2), or, b) you throw an error ("Whatever this is, it's not an invoice!"). You would have to do that no matter what, because you could be getting anything, regardless of what the version says, or the media type says, or what anything else says.

    You can make your payloads forward compatible to be resistant to breaking change, then version is a matter of making use of all the information you can see. If you get A and B, then while you'd like to have C and D as well, the clients can get by with the more limited information. Of if the clients see C and D, they would know to ignore A and B, as that data is deprecated. Same with the server. If something is sending A and B, it's implied to be an older processing model than if they sent along C and D.

    You can version through rel names "order" vs "order_2", old clients only know to use "order", new clients know to use "order_2" and follow that link instead.

    Or you simply include a version identifier in the payload, that's an easy check as well (especially since it's early in your design).

    There are a lot of ways to manage the versioning, but the media type really shouldn't be the mechanism. That's why this really isn't a "problem" with ATOM. So, it's a matter of perspective.

    I have another discussion about the Accept header over here: REST API having same object, but light

    This (IMHO) unrelated to your versioning issue, but it's an example of extended media-types. But that's only my perception of why and how most folks want "versioning". A case could be made this case is the same thing, but most folks associate versioning with services, not simply data representations, which this other post was mostly about.

    In the end, either your client and/or server are flexible enough to handle versioned data or they're not. They will (mostly, they are computers after all. Deterministic my heinie...) do what they're told. A simple rule of "ignore stuff that you don't know" can take you quite far in terms of versioning without ever changing a v1 to a v2, regardless of your encoding. Likewise "work with what you have" is a nice rule for a flexible, tolerant server. If you have problems in either case, that's what errors, logs, operators, and 24hr pagers are for, and you need those anyway.