7 ms·
Best practices for API versioning?
- kevinpet 13y agoI'd like to see the subset of the answers from people who have actually maintained a successful and widely used API. I'm skeptical that most clients are sophisticated enough to deal with the pure REST dream. Specifically, changing the JSON representation of, say, a list of account activity, is perfectly compatible with REST. "Oh, you want /account-activity, here it is, why yes, it does happen to be a list of links to /account-activity/messages and /account-activity/transactions instead of the flat list of things with a 'type' field like it was last week, but I'm directing you to the resource, so it's all good"
- zimbatm 13y agoFrom my experience, you can go a long way by just adding new HTTP end-points and extending the JSON with new attributes with no versionning. By the time you really want to change things it probably mean that you understand the domain well enough to make a new better service.
- jcampbell1 13y agoExactly. He suggests 301 redirecting requests. The is going to be a giant bag of pain for anything other than GET requests.
- thibauts 13y agoThat's exactly what I thought. I can't help feeling this is yet another "do it this way" post by a person that is big on theory but probably very low on real life in-the-field practice. You can't advise 301 when you had to actually implement it. IMO this post does more harm than good by misleading its readers. It reminds me a lot of (sometimes very well known) people that feel entitled to teach you how to write javascript SPAs after trying their hand at the most simple case, like a todo app. The incentive to be seen, known, collect points here or there is totally biasing the game.
- danellis 13y agoNo matter how large, successful and technical your client is, they'll undoubtedly have their least HTTP-literate monkey developing against your API. It gets awkward having to explain basic things to someone you started out assuming was your peer.
- aantix 13y agoWe found that our API and web controllers were doing the exact same thing, so with rails we're just hitting the same endpoints but just using respond_to to differentiate whether we respond with a jbuilder view (json) or standard HTML. For the versioning, we developed VersionCake. https://github.com/bwillis/versioncake/ https://github.com/bwillis/versioncake/ For each jbuilder view, the version number is contained within the file name. E.g. index.json.v1.jbuilder index.json.v2.jbuilder index.json.v4.jbuilder The client passes in a version number and VersionCake intelligently renders the appropriate view version or degrades to a prior version when the specified versioned view doesn't exist. Works like a dream.
- jrnkntl 13y agoHappy user of VersionCake here! I am using it to support older versions of our iPhone apps while our backend can improve and expand without problems. Thanks for your work!
- ams6110 13y agoPure REST can be a roadblock in the pragmatic real world. I've impemented "REST" API calls in client platforms that could do GET and POST and that's it. No PUT, DELETE, etc. But since the API allowed deletes via a POST action, it was possible for me to achieve what I needed to do within those constraints.
- icebraining 13y agoThere's nothing inherently unRESTful about just using GET and POST.
- steveklabnik 13y agoFielding literally has a blog post on the subject: http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
- pbreit 13y agoI worked on and supported a pseudo-API at PayPal for 7 or 8 years..."Web Accept" and "IPN". 10s or 100s of thousands of integrations. 10s or 100s of millions of payments. No versioning. It works really well. Once you add versioning, it's too tempting to add breaking changes and just keep versioning. It becomes a total mess for everyone.
- yannk 13y agoI'm tempted to both agree with you and curse at you. Yes, I agree with the idea of enforcing backward compatibility as much as possible and, when that fails, make it a social problem (documentation, email, advance notice...). But being in the midst of one of those PayPal integration... oh my, what a mess! * The sea of DEPRECATED in the documentation (hide it away?) * The pervasive inconsistency in everything * The choice of x-www-form-urlencoded for IPN data... (I'm trying to figure out some charset bug at the moment) Anyway... :) No hard feelings!
- almost 13y agoPayPals API certainly isn't the worst but it's pretty awful. So much pain doing PayPal integrations. I assumed it just kind of happened but I'm shocked if you guys thought it was good... See Stripe for an example of not to abuse your API users.
- StavrosK 13y agoStripe is an example of how to be awesome overall.
- pbreit 13y agoIt's not perfect but I'd like to point out that it was born in 2000 and still works fine today 13 years later. I think all the sample code I wrote is still in the PayPal docs somewhere. The most painful PayPal integrations for me were the SOAP ones.
- icebraining 13y agoNo. There's nothing RESTful about changing the data format without notice - on the contrary: REST implies that media types should be well defined, and that includes specifying those issues. There's nothing in REST that opposes versioning either. All it says is that you shouldn't use the URLs for it, since then if the client wants to upgrade, it'll break all his stored links. Versioning can be handled just fine by having the client request the media type with the version he needs, e.g.: Accept: application/users-list+v1 And if it asks for that, you should definitively give it the v1 format, not throw the v2 and let it deal with that.
- brianbarker 13y agoThis is the exact problem Hypermedia APIs set to solve. The second answer even chimes in with that.
- LukeB_UK 13y agoThe top answer seems to have the mentality of "screw the consumer" when it comes to versioning. If you have someone using version 1 of an api, when you decide to make it version 2, you're essentially breaking their app by having the reference change to the new one. The answer even touches on the subject that it should be a permalink, yet they then say that a new version should be the same url as the old one, which means it's not a permalink! In my view, this is madness and potentially breaking for people using your api. In my personal opinion, you should set out with versioning in mind from the start, otherwise by making even a slight change to your api, you change what the consumer receives, potentially breaking stuff.
- icebraining 13y agoIt should be the same URL as the old one. Just not the same content type. The user /u/john of version 1 is the same user /u/john of version 2. You don't want to break all links just because the client upgraded. Just reply with the appropriate format for the version the client is asking for.
- euphemize 13y agoAgreed with kevinpet, I'd like to hear from people who built large, public APIs. In my experience of doing this in the wild, it usually goes like so: you develop a set of API calls, a client that talks to that API and release the client. Then your director of product demands a change which requires a modification in the data model. You need api versions to avoid your old clients crashing if they use the new data model. Eventually your data model stabilizes and you don't need versions so much, but in my experience, it can take years.
- danellis 13y agoIdeally your data model is identified by the media type, which would look something like 'application/vnd.yourco.foo.v2+json', but in reality that will get you a lot of blank stares.
- keypusher 13y agoWhy put the version in the URI at all? Can you not just use headers to request and return particular version? Is there a downside to this approach?
- simlevesque 13y agoAs others have pointed, if you try to POST a HTML5 form to any API, you can't specify the header, it will always be x-www-formurlencoded.
- sureshv 13y agoWhy not use a custom type instead of the 'Content-Type' header? Edit: Looks like this may not be possible on a HTML 5 form.
- wprl 13y agoThe thing is, this isn't all that important...
- wprl 13y agoA down vote isn't a substitute for a valid reason why supporting vanilla forms is an important point in the API versioning discussion ;)
- begriffs 13y agoMaybe posting should be done with a richer client-side app than a vanilla form. The unaided browser is a pretty sad user agent.
- waterside81 13y agoAs others have alluded to, it's all fine & dandy to try to adhere to the guidelines of "pure REST" but in reality, you have customers/users who know enough to construct a web request, but don't really understand what HTTP verbs are, what HTTP headers are etc. In my experience, your average developer (of course, not anyone who'd frequent HN!) really doesn't have a good grasp of how HTTP works. So the way you combat this problem is either 1) You provide client libraries in the popular languages so your users can download those and not have to make requests directly. This way you can put versioning and accepted response types in the headers instead of the URL OR 2) You put everything in the URL and you're done with. In my company, we initially went with 1) but then realized that just putting a version & format (.json, .xml etc.) in the URL lead to much fewer "Your API isn't working" emails.
- deleted 13y ago[deleted]
- johns 13y agoI think you misinterpreted the parent post. You two are almost certainly talking about the same kind of REST aka the mainstream understanding of it popularized by APIs like Twitter, Twilio, Stripe, etc. The "real/pure REST" folks are talking about by-the-dissertion Fielding's REST which is far more complicated and confusing and is probably not a good idea for a public API. It goes beyond verbs and pretty URLs. Look into Hypermedia APIs. That's the One True REST.
- MichaelGG 13y agoYeah, Twilio's API isn't REST as it has versions and content types in the URIs. Plus, you're supposed to construct URIs (and all the clients do this AFAIK, since it'd be idiotic waste of a roundtrip not to). E.g. "/2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{IncomingPhoneNumberSid}" Yet it's the closest thing I've seen held up as a "good RESTful API". In practise, it just means I've got to write more code to shove parameters into the URI and body. "REST" as used by pretty much everyone is just a codeword for "we didn't use SOAP!". And maybe the URLs are more than just "/FooService" and maybe they use HTTP verbs.
- userbinator 13y agoThe only thing I'd consider to be close to a "best" practice is to maintain backwards compatibility. This means a little more effort initially put into the design, but it pays off if it's to stay around for a long time.
- yaur 13y agoand what if the guy before you didn't do that?
- pan69 13y agoThe version thing is interesting. How do you maintain various current versions of an API? If a URI cannot contain a version number (as the purists would claim) then versioning happens in the underlying code base? If so, a client still would have to specify a version, either in a query parameters, request header or somewhere else. In reality, if an API doesn't contain a version number in it's URI will almost be impossible to maintain. To give a simple example, what if you decide to switch technologies between API v1 and v2? How would you deal with that? Some sort of abstraction layer I'd guess... As far as I'm concerned, a version number in the URI makes the most sense. They way I look at is is that /v1/resources/123 is a different resource than /v2/resources/123. So far, using a version number in the URI has worked out quite well for me over time since I can isolate various code bases containing different versions. Something that would not have been possible otherwise.
- dllthomas 13y agoMy understanding is that request header is the most standards compliant way. In principle, you could certainly determine how you interpret the URL based on that. In practice, both clients and servers aren't really designed for that, though. Particularly browsers, if you expect people to want to access/explore your API that way.
- dragonwriter 13y ago> If a URI cannot contain a version number (as the purists would claim) then versioning happens in the underlying code base? It happens in the underlying code base whether or not it is reflected in the URI; one "pure" REST would have it in the Content-Type, but that's problematic with pure-HTML forms.
- pbreit 13y agoDon't version. You'd be surprised how easy and powerful such a thing is.
- _nato_ 13y agoI think there is a lot of whimsy in what Don Knuth chose to do to version TeX -- using pi (3.14) and moving the precision to the right upon every (though, not often) version bump. Probably a little impractical for the rest of us, but it's my favorite! `So creative!
- joaomsa 13y agoI don't agree with the top response's recomendation of using redirects to represent sunsetting of versions. That behaviour breaks POSTing. [1] > Note: When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request. [1] https://www.ietf.org/rfc/rfc2616.txt https://www.ietf.org/rfc/rfc2616.txt
- dragonwriter 13y agoIt breaks behavior in some misbehaving HTTP/1.0 clients at the time the HTTP/1.1 RFC was written. I'm not sure that that's a signficiant concern now. Still, if the behavior has changed, 301 isn't appropriate for other reasons, and if it hasn't changed, you shouldn't need a redirect at all, just handle requests to the old URL.
- leepowers 13y agoI always thought API versions in URLs had more to do with efficient URL routing than RESTful semantics. If you're Twitter or AWS and handling tens of millions of requests a day it would (seem) far less resource intensive to determine routing based on a pre-defined URL structure than to inspect the headers of each incoming API request. It might also save development time. So you have your block of v1 API servers and your block of v2 servers, without having to re-code the v1 stack to also parse v2 requests.
- schrodinger 13y agoIs it really any more resource intensive to inspect a header vs a url? Why?
- girvo 13y agoUneducated guess: The URL is always looked at, and further up the stack too, whereas headers might not be passed down to the client explicitly?
- leepowers 13y agoYou can scan a URL very quickly: http://myapi.mysite/v1/user/new http://myapi.mysite/v2/tweets/fetch The version identifier is always in the same place. HTTP headers come in an unordered list. And a client can send any number of headers to the API. And the version identifier may be at the beginning of the list, in the middle, the end, any position really. Even so, I doubt the resource usage is very different per-request. But writ large it may make a difference.
- icebraining 13y agoGNU Grep can search for a string in 41000 files in 1.2s (that's with open(), mmap(), close(), etc). I really doubt that web frameworks are so optimized that matching a particular header would take any perceivable time.
- mrec 13y agoI wouldn't worry too much about the cost of parsing. I'd be a lot more concerned about APIs that rely on proxy and UA caches for performance and/or scaling. A request has a much better chance of getting cached reliably if all the important semantics are in the path than if they're in arbitrarily-ordered querystring args or headers. Squid's default config didn't cache anything with a querystring up through v3. OldIE wouldn't cache an uncompressed response with any Vary headers. I suspect there are landmines like this buried all over the place.
- pbreit 13y agoI've never really cared for API versioning, especially the "/v1/" or whatever in the URL. Come up with a decent API design from the beginning; commit to backwards compatibility, non-breaking changes, thorough testing and fast fixes; and stick with it for a decade or so.
- unabridged 13y agoThrowing out backwards compatibility should be an absolute last resort, you may as well start a new project that is written clean from the beginning. No function should be promoted to the core API without serious consideration. Instead use something like /alpha/function-v2.2/ for testing functions that may disappear or be updated, making it easy for clients to write workarounds for these non permanent functions.
- Tohhou 13y agoIt's better to be able to start from a clean slate in the future while still being able to support legacy versions if it is advantageous to do so. You are not smart enough to do it right the first time, because you cannot predict what changing conditions the future will bring.
- joncrocks 13y agoWhich is fine as long as you aren't planning to change your product at all. Yes, you can try and put it off for as long as possible, only making backwards compatible changes. But it's likely at some point you'll need to change something in your API, that simply won't work with existing clients.
- pbreit 13y agoIt's almost always possible to update APIs without breaking backwards-compatibility.
- yawz 13y agoI thought we'd already cracked this nut thanks to content negotiation. Obviously not.
- begriffs 13y agoHow is this even a debate? Versioning is a form of content negotiation just like encoding, language, mimetype, and charset. Why treat this one extra property any differently? http://blog.begriffs.com/2014/02/api-versioning-best-practices-no-really.html http://blog.begriffs.com/2014/02/api-versioning-best-practic...
- bpedro 13y agoYou might also be interested in this article: http://apiux.com/2013/05/14/api-versioning/ http://apiux.com/2013/05/14/api-versioning/
- Sarkie 13y agoThis might be useful to look at from a real world point of view: http://davidwhitney.co.uk/Blog/2013/10/27/lessons-learnt-scaling-a-big-mainstream-restful-api/ http://davidwhitney.co.uk/Blog/2013/10/27/lessons-learnt-sca...
- rjzzleep 13y agoyou guys might be interested in the apigee api-craft google group, which discusses a lot of these topics https://groups.google.com/forum/#!forum/api-craft https://groups.google.com/forum/#!forum/api-craft
- royjacobs 13y agoI have to second this. It's an excellent resource filled with sane people.
- cullenking 13y agoWe run an API with probably 20 registered consumers, supporting apps and websites with different versions. This is not by any means an example of "the one true way", but it seems to be working well for us. Basically, we put together a simple wrapper that lets us automatically pick which API version to use based on a users apikey, registered version (I set them up with v1 or v2), and what they pass in. A before_filter automatically pulls the apikey and version from params/headers, and it's consumed like this: https://gist.github.com/kingcu/1e5e6982ad3e05707303 https://gist.github.com/kingcu/1e5e6982ad3e05707303 Essentially this lets us be strict about what key gets what result, and, lets us do prioritized fallback logic by using the any() function shown.