Hacker News new | past | comments | ask | show | jobs | submit login
Version your RESTful API responses (hribar.info)
26 points by yuxt on Sept 26, 2012 | hide | past | web | favorite | 51 comments

The whole point of a REST API version bump is that it is an artifact of introducing necessary breaking changes (either removing an endpoint at X path, removing Y keys, or even serializing dates in Z format).

Though there have been many detractors, I still think URL based versioning is the best way to go. How do you version a header on a resource that goes away or pops into existence? What if I just ask for "application/json" or the "freshest" due to ignorance and you move the target and get even fresher? There is very little practical benefit to versioning in a HTTP header except some argument about URI purity.

I feel it best to supplement your REST API with a pure-URI scheme available on _all_ of your resources that is dependent on the request type. You might have both;

https://api.test.com/v1/resource -- standardized API response https://test.com/resource -- "pure" object if JSON is requested, pretty HTML-wrapped version if not.

You can rely on the versioned API to have a specific format and metadata (things like number of results, query times, etc), while the "pure" version is just a raw JSON object.

If you're writing a client which is sensitive to the resource representation changing over time and you're using something that calls itself an API, hopefully one would skim the provided docs enough to see that you might want to explicitly state the version you want. Or notice that the Content-Type returned for the generic application/json query is a versioned type.

I acknowledge that it's a little more work than just banging out a versioned URI, but it's not that much work and I like the URI/conceptual purity.

> and I like the URI/conceptual purity.

This sums up pretty much the whole debate. Pragmatics vs idealists.

The interesting thing is that it is a false dichotomy - it's easy enough to implement both. Perhaps enhanced by adding a flag on the documentation to flip between pragmatist/idealist so each only sees their one way to do it right...

Another solution that I've seen is to use a custom header in the request such as X-Api-Client-Version: 1

If the header isn't provided, then the most current version of the API is called. The nice thing about this is that it doesn't 'pollute' the URI and it allows for automatic upgrading to the latest version of the API if the developer doesn't specify the API version.

Silently and automatically upgrading an API is a horrible idea.

It depends on how your API is designed. If it's a tightly coupled RPC-style API or something, this is obviously a bad idea because you'll break every client that didn't see the change coming. But the goal of designing APIs in a hypermedia style is to eliminate this tight coupling and include in each response all the information that a client would need to traverse the application's states. When this is designed properly, it is easier to change the API's functionality without breaking existing clients.

The web is a great example of this (although you may have to squint a bit to see it). Browsers don't need to add additional code or install plugins to handle forms with different fields or links to content of different types, because the semantics of those elements and their interactions are well-defined.

If you're requesting a sub-resource without navigating to it from a known, fixed root, you're not doing HATEOAS. If you're not doing HATEOAS, then that's level 2 REST. The upshot is that you have to have stable URIs, and therefore you have to version them. If you make the extra effort, you get complete freedom to rework your URI schema at any time.

While this seems like a good solution, I'm not sure the problem is real: to me, a v1 API and a v2 API are two separate APIs, so the URLs for that API aren't really changing during it's lifetime (ie. the v1 stuff will stay the same for those continuing to use it). I think this is just a mis-interpretation of a core REST/web principle that the URIs for a specific resource in the API shouldn't change (hence creating a v2 or other sub-directory if you're going to change the output or functionality).

Ok, therefore:

  GET / HTTP/1.0
  GET / HTTP/1.1
should be

  GET /v1
  GET /v2
and YOU'd have to tell the browser which site uses which version.

Wouldn't it be nicer if the browser could tell the server which version of the interface it understands?

It happens that the metadatum follows the URI in HTTP requests. The author can't do that, but he can use a header field to achieve the same.

Hence the idea to use "Accept:" for versioning.

That's not the same layer. HTTP versioning is on the transport level.

All that the author is going to achieve is to have broken client codes every time he's going to bump his API version because most developers won't bother to check the content-type. Or he has control of both the client and server code and then in that case it doesn't matter.

N.B.: yes it's the same layer. HTTP is at the Application layer in the Internet layer model (and this is the Internet, not your school's Network class where they dream about OSI layers).

It's irrelevant which layer this is happening at. The point is that there is a resource address and a resource version. Decoupling those allows you to retain the same resource address for different resource versions - whether this is done for backwards compatibility, multi-language support or other reasons.

"All that the author is going to achieve is to have broken client codes every time he's going to bump his API version because most developers won't bother to check the content-type."

The point of the article was in fact to teach those mediocre developers who "won't bother", to actually bother to understand these concepts.

HTTP versioning is part of the HTTP protocol which is at the Application Layer in both the Internet Layer Model and the OSI Layer Model.

Educate yourself: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol

> Wouldn't it be nicer if the browser could tell the server which version of the interface it understands?

It would be nice! However, many packages people want to develop clients for REST APIs with have terrible HTTP support, such as Flash. If you ever see an API that supports a "method" parameter in the URL or POST body, it's because Flash is terrible.

Depending on what your requirements are, you may not need to support every outdated HTTP library in existence.

Sure, if your URIs are versioned those are by definition not going to change. However, I would stress that the 'R' in URI stands for "Resource", not "particular version of an API to request that resource". It seems like your approach places the implementation details of requesting a resource over the actual identifier for that resource.

It's useful semantically to think of different versions of a resource as different resources. It makes reasoning about them free of any assumptions the previous versions may have introduced.

I'm not dismissing the idea that resources need to be versioned, certainly a client written against v1 might not be able to handle the v2 representation of the resource. It's just that I think the abstraction the resource is representing generally doesn't change just because the implementation details behind it have.

If you change the way a "customerId" is formatted in V2, the idea of providing a "customer" resource hasn't changed, and as a matter of style I think it's cleaner that the URL for the customer record representing a single person doesn't change or suddenly split into multiple resources which really represent one entity. IMO the header-versioned approach expresses the semantic difference you point out cleanly, and in addition does not come with undesirable side-effect of diluting the URI concept.

I don't get this diluting the URI concept. Different versions are often going to have more differences than simply representation. They may provide additional features and have slightly different semantics. It's not the same resource.

And even if it were, do you guys propose that if I were to request the same data represented in slightly different ways (say order data grouped by customers or grouped by date) within the same version, I should use headers to distinguish between the two, instead of a query string?

I would say it emphatically is the same resource, in the same way that content negotiation can return you either a JSON or HTML representation of a given resource with the same URL, for example.

I don't have a strong opinion on query strings but I would suspect performing various actions on a given resource or collections of resources could be handled just as well with plain URLs: /customers/byDate /customers/byName. To me those represent resources comprised of differing collections of customer resources. I'm not sure there's a substantial difference there.

Right, I didn't mean to introduce query strings as a third option (to me it's part of the URL), but if you think it's okay to have two different URLs for orders by date and orders by customer name, which can be merely differences in representation, not data (each order can still have the full information, representing the same information), why isn't it okay to have two different URLs for two different versions, which may differ in much the same way?

I think you're still conflating resource with representation.

I would consider /customers/byDate to be a resource. It happens to be a collection containing references to other resources (customers) which you can then navigate to directly if you want. You can get the byDate collection/resource in varying represenations; JSON, XML, etc. Same with the individual customer resources contained within the collection.

Here's a thought experiment. Say you write a client for version 1 of a URL-versioned API. While we're at it, let's encode representation in the URL as well (/api/v1/customers/1.xml, /api/v1/customers/2.json). Say your client stores references to these resources, maybe the user can add them to a list of favorites. Now there's an API upgrade and you point your client to /api/v2/*. Do you just go in with a regex and munge the stored URLs? If you call what you're doing REST, now you're breaking HATEOS. What if the URL scheme changes with the new version? Seems quite likely when the developer already doesn't subscribe to the notion of having a single URL for a resource. What if you want to store a representation-neutral link to a resource and the user can toggle their view between them? Do you make the assumption that you can munge the "extension"? That's a lot of brittle, in-band assumptions to make with URLs which are supposed to be opaque identifiers. Seems more flexible to have separate, well-defined mechanisms for handling this stuff.

Disclaimer: I don't have a lot of experience in this area, I'm just arguing for what makes sense to me to explore the ideas. I also acknowledge that plenty of sites use URL-versioned and content-negotiated schemes and have little interest in arguing what's "true REST" but it seems to me that following a stricter interpretation of these concepts does come with some nice benefits.

You've changed my example considerably. /orders/groupByCustomer and /orders/groupByDate have the exact same data. The only difference here is representation.

I would expect that API changes from version 1 to version 2 to often have even more differences than above. They may support different methods, may expose different sets of data, etc. There's no reason why they have to be thought of the same resource.

As for your thought experiment, why not have a version-independent URI that points to the latest version? I don't even understand this whole representation-neutral link - you're going to pick one representation or another. Either the client has to give the version information in a header or an URI. Header manipulation is much harder to deal with in practice and isn't easily supported by every client.

I don't think I've changed your example, I just don't agree with this:

>/orders/groupByCustomer and /orders/groupByDate have the exact same data. The only difference here is representation.

Those are two different resources (made plain within the context of my approach by the fact that they have two different URLs). Those resources are ordered collections. They contain the same totality of resources within them, but ordered collections are not semantically equal if the order is different. Therefore I argue those are two distinct collection resources. The fact that they both contain the same resources is irrelevant because the ordering is central to the identity of the resource here.

The URL is not the representation no matter how you look at it [1]. `/orders/groupByCustomer` identifies a resource which could, for example, give you an XML representation, a JSON representation, a PDF representation, etc., depending on content negotiation.

An API change which introduces different methods and data is fine. A resource returned under the new version will expose that, old versions won't see it. There's no reason why the resources can't be the same across versions, but maybe a request for v2 of Customer returns a zip code now and v1 doesn't. Still the same Customer and you have one URL for it.

As for a version-independent URI that points to the latest version (assuming no version in the header), the argument made elsewhere here is that it can break clients that use it when the API is changed.

> Header manipulation is much harder to deal with in practice and isn't easily supported by every client.

I don't think it's that much harder. Granted, if you're dealing with existing clients that only support a small subset of HTTP your choice of style might be made for you. If I'm designing a brand-new API I think I'd be inclined to say "it's 2012 and this is an HTTP service, deal with it".

PS, Thanks for the interesting discussion. It's definitely making me think this through more deeply than I have in the past.

[1] http://en.wikipedia.org/wiki/Representational_state_transfer...

> I think the abstraction the resource is representing generally doesn't change just because the implementation details behind it have.

The concrete behind the abstraction may not change, but URLs don't point to concretes, they point to representations of concretes. If "customer" in v1 returned billing information and names while in v2 just returns GPG public keys and a JPEG of their face, they're not really the same resource even if they're representations of the same customer.

I'm not sure that we're in disagreement here, really. The concrete implementation will change, that doesn't mean the abstract resource changes.

If you've got two different resources representing different aspects of customer data, those don't need to be a separate version because they're distinct resources.

How often do you update the major version of your API ? Most of the time you can just augment your API with new end-points and keep the old paths around for backward-compatibility. When you're ready to change the major version you might as well start a new service from scratch with everything that you have learned.

Now that all your responses have a non IANA-approved[1] content-type, standard clients won't see it as JSON by default. Your curl requests are longer to type because you have to add this '-HAccept:application/vnd.something+json' string to your them. The reality is that bad developers will have broken clients when you update your API in any case.

I'm grateful that the REST movement brought attention to follow the HTTP protocol more closely but in practice, nobody writes truly RESTful clients. For that you would need to write a client that follows URLs passed in the body or in the headers. For example if you implement pagination, you're supposed to fetch page 1 and then follow the links until you're at the page you want. In reality you just make a page= attribute convention that avoids all these unnecessary round-trips.

[1]: http://www.iana.org/assignments/media-types/index.html

I personally prefer this approach - certainly if the changes you're making are only to the representation of your resources, then (assuming they can't be made purely additively, i.e. without breaking existing clients) simply making a new content type available seems the natural way to go.

If it's of interest, I wrote a couple of articles about how we implemented exactly this sort of versioning in Rails for our app's API:

http://techblog.tribesports.com/blog/2011/09/24/versioning-t... http://techblog.tribesports.com/blog/2011/09/24/separating-a...

I think these demonstrate the advantages of content negotiation as a versioning mechanism. To introduce a new API representation, we simply have to define a new set of decorators for our exposed resources, define a new MIME type and we're set. No monkeying with our routing, no messing around in controllers; a view-level change entails only view-level code alterations.

I don't hate the idea or anything but I don't see what problem this solves in practice?

Agree ... normally the V2 API comes out with major revisions, such that it would not work with someone that coded up an app based on the V1 API. Using /v1/ and /v2/ allows customers to transition over some set amount of time. There is some value in keeping things simple for your end-users.

If the customers use 'Accept: application/vnd.example.v1+json' and 'Accept: application/vnd.example.v2+json' they can also transition over in the same manner.

It solves the problem of client-server communication. This is the same idea as with: "GET /car HTTP/1.0" and "GET /car HTTP/1.1" . It provides a clear way to specify which protocol the client speaks, while allowing the URI to stay the same. Contrast this with: "GET /v1/car" and "GET /v2/car".

The article simply uses the Accept: header to achieve the same.

But how is this better in practice? Why is it important that the URI stay the same? I don't see why coding the version information in the header is better than coding it in the URI. The latter seems much easier to manage in practice.

If you leave the version out of the URL, the same URL can represent multiple versions/renderings of the same resource.

This makes sense conceptually, as /users/22 is the same user, whether you are getting the data in version 1 of the XML rendering, or version 6 of the JSON rendering, or even an HTML page or snippet representing the user. The Accept header can handle all of this.

There is also the advantage that when returning links to other resources -- e.g. GET /users returning an array of users, each specified by a URI like /users/22 -- the server does not need to prescribe a version for the client. The server just returns the link to the resource, and the client Accepts whichever format/version it wants.

I would contend that API versions are a fundamental part of a resource. A resource is defined by its content and available operations. Changes to an API redefine the content and operations of resources. That redefinition makes different API versions distinct resources from each other.

Also, api clients shouldn't give a damn about URI structure/content. If they aren't using links embedded within the representations you present then your "RESTful" API has bigger problems than the where-to-hide-the-version game.

I would contend that API versions are a fundamental part of a resource. A resource is defined by its content and available operations. Changes to an API redefine the content and operations of resources. That redefinition makes different API versions distinct resources from each other.

Consider that a server may choose to provide the same resources in one of multiple versions and renderings, for instance to support older clients alongside new ones. I would consider it surprising if altering a resource at e.g. /v1/users/22 would also alter resources at other URLs, like /v2/users/22.

In addition, keeping version out of the URL allows the resources to be cross-referenced between different client apps, which are not necessarily browser-bound. One client expects the latest and greatest version, one client expects an older version, and they can pass data back and forth freely.

Also, api clients shouldn't give a damn about URI structure/content. If they aren't using links embedded within the representations you present then your "RESTful" API has bigger problems than the where-to-hide-the-version game.

I agree, but I'm talking about returning e.g. /users/22 instead of /v1/users/22 or /v1/users/22.json, not instead of 22.

In this example, the client knows what version it can handle and should specify that separately than the user providing the URI for the desired resource. Separation of concerns. Why make the end user guess about version numbers?

Why is the end user putting in the URI for a web service? I'm confused. If the client knows what versions of the web service it can work with, the client can also know the correct URIs for different versions.

Ah, you're right.

I like this idea a lot! I'm curious if anyone else finds major flaws in this idea but it seems to make a lot of sense to me. Yes, it may change slightly how you do your calls, but if the logic works and that's the "worse thing" that could come from this then I think it's worth implementing.

* You cannot test this from the browser.

* Many, many client developers will be confused by this approach. Also if they're using a shitty http library (often, yes) they'll hate you.

* What happens when someone sends an "Accept: application/json"? If you default to "send the latest" developers will fail to read your docs and get pissed when your next version breaks their code.

* His citation of Fielding's dissertation at best fails to support his claim and at worst is a misunderstanding of it. How is "put this in your accept header" any more of a uniform interface than "this thing goes in urls"? If you're doing HATEOAS[1] properly the answer is: "accept headers are less general and thus worse". If you include api versions as a fundamental part of the notion of a resource they seem to become important enough to need top level (e.g. URI) support.

All in all this seems like a thinly veiled argument for URI purity for its own sake. API versions change the definitions of what a resource looks like and what you can do with it. What more is there to a resource? What benefits, aside from sweeping the versioning mess under the header rug, does this provide?

[1] http://en.wikipedia.org/wiki/HATEOAS

URIs should be black boxes. My understanding is that the client should need no understanding at all of their content.

I agree. This is only possible when all resource access flows from a predefined "root" resource via hyperlinks. If you're doing that, why force (arguably more difficult) accept header handling on your api clients? Then they have to set the accept header correctly at every request instead of getting the version number right once when retrieving the root document. Beyond that debugging now requires access to / a record of the accept headers involved, and returning the latest version for plain application/json creates a very nasty bug for clients.

I don't think REST meaningfully comes down on either side of this idea. I do think practicality sits highly in favor of versions in URIs.

Setting the same Accept header at every request is not difficult or error-prone.

But that aside, what happens when you store versioned resource URLs in the client, and then you want to upgrade your client to a new version of the API? Do you just lose all of your data from the old version, since you don't have a single URL for a resource?

One of the main drawbacks to the HTTP Accept header approach for both versioning and content negotiation is that it makes it less convenient to test from a browser when you can't encode those things in the URL.

Personally I feel that using the header feels more "right" and lines up with the available mechanisms of HTTP. I think the inconvenience could be easily overcome with browser plugins, for example.

You can always add a version=v1 to the query parameters and use that as an override when performing content negotiation. It's still not terribly convenient.

Seems to me he is abusing the Accept-header. In his example, the application provides a JSON-response, but with a vendor-specific media type. Once the API version changes, he changes the media type again.

This is both impractical because if the client application consuming the content happens to be a web browser, it will not see JSON and at least to me seems like a major flaw: if it is JSON, use the appropriate media type.

If you need to fiddle with HTTP headers, probably a X-API-VERSION header would be more logical instead of clobbering the media type.

I don't think "media type" is synonymous with "data format". For example, IANA considers atomcat+xml and atomsvc+xml to be distinct media types [1], despite them both being XML, and there are 406 other XML-based media types in that list.

[1] http://www.iana.org/assignments/media-types/application/inde...

Some libraries that are used to connect to versioned resources like this don't support these types of media types. I tried to version my RESTful APIs exactly like this and ran into integration problems with an externally developed client which was based in Flex. It could have been our contractors not knowing Flex all that well or it could have been inflexibility in the language libraries itself (pun intended).

With just a bit of forethought and a little planning, an API almost never really needs to be versioned in either way. One of the problems with versioning is that it almost encourages changing the API and most consumers are slow to update, if at all.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact