10 ms·
How to Version an API
- BillinghamJ 9y agoCurrently I structure my URLs like this: /v1/2017-06-08/blah The idea is that you write in the date that you wrote the implementation against the API. This is just like Stripe's API, but explicit rather than implicit. Then everything under that will continue to work as-is. There's a few reasons I like to do this, but the main one is that I often struggle to decide whether a given change is really "worthy" of a major version change. The only rule/caveat we have is: within a given client's implementation, you should only use one date at a time. Mixing them creates a small possibility of compatibility issues. The initial /v1/ is just in case I decide to move away from the date thing at some stage.
- edoceo 9y agoI'm similar but just prefix like /v2014 I assume I won't make multiple versions in a year. I have a current system with /v2014, /v2016 and /v2017 I like to smash dates into all my version strings
- cyann 9y agoYes, human readable dates are very good at conveying a sense of obsolescence. Mine are a bit more granular: /vyymm, e.g. /v1503, /v1610, /v1706. This should work well for this century ;-)
- hdhzy 9y agoRoy Fielding himself had an interesting interview [0] where versioning a REST API was one of the topics (spoilers: version is not in the url nor headers). [0]: https://www.infoq.com/articles/roy-fielding-on-versioning/ https://www.infoq.com/articles/roy-fielding-on-versioning/
- eriknstr 9y ago> My point was that there is no need to anticipate such world-breaking changes with a version ID. We have the hostname for that. What you are creating is not a new version of the API, but a new system with a new brand. I think Rich Hickey (the creator of Clojure) would agree. He said something to this effect in the "Spec-ulation" keynote at Clojure/conj 2016. https://www.youtube.com/watch?v=oyLBGkS5ICk https://www.youtube.com/watch?v=oyLBGkS5ICk
- deathanatos 9y agoI'm going to have to disagree with him. If I need to break compatibility on a single resource, I don't want to mirror every API endpoint (except that one) to a new hostname. That becomes continually more and more complex and hard to keep sense of as changes progress. The hostname thing might work for huge, world-changing re-writes, but for small, incremental changes, I think something like what GitHub does w/ the Accept header is far more appropriate.
- prebrov 9y agoHis argument is for API discoverability. Client should be able to learn about available endpoints [edit: ...and resources. And be able to comprehend resource record conventions]
- deathanatos 9y agoI also don't see the point of API discoverability, at least, not programmatically. Sure, such a thing could be built, but I don't know what could be done w/ the results. Most API's I've ever worked on required domain knowledge in order to operate on (e.g., you had to understand the content — semantically — of the result. I.e., you needed to understand what a `applicaiton/vnd.bigcorp.widget.v2+json` was.) I just don't see any generic operations w.r.t. discovery that are … useful. I'm not trying to say that you shouldn't, e.g., use URLs in your resulting resources; e.g., if a query for a widget returns: { "widget_name": "NextGen Enterprise Widget", "suppliers": "http://bigcorp.example.com/widget/123/suppliers" } That's fine. But you're still going to need to know how to parse the result of …/suppliers, or it's just opaque bytes.
- aantix 9y agoIf you're a Rails dev, take a look at VersionCake. It allows you to version your json Jbuilder views by adopting the file naming convention (e.g. show.json.v1.jbuilder, show.json.v2.jbuilder, etc). There's fallback to most recent, compatible version of the API. https://github.com/bwillis/versioncake https://github.com/bwillis/versioncake
- pchristensen 9y agoI was like, Who's recommending VersionCake? But it's none other than Jim Jones!
- m42 9y agographql
- dsp1234 9y agohttps://developers.facebook.com/docs/graph-api https://developers.facebook.com/docs/graph-api "The Graph API has multiple versions available"
- vincentmarle 9y ago> When you construct an API, you receive scalability and load balancing benefits by decoupling your API and hosting it as its own backend I've thought about doing this a couple of times but it seems like a lot of overhead if you need to have separate production instances running for each version of your API. Is there a better approach to organize this in a more scalable manner?
- rockostrich 9y agoThe two options are have the different versions running on separate instances or have all versions in a single instance. The former lets you scale the versions independently. If v1 traffic drops 5x overnight then you can scale it down by 5x. The latter simplifies the deployment and management though. You just have to deploy one service and scale one service to a single amount of traffic.
- taormina 9y agoAt my last job, we used https://retailmenot.github.io/shield/ https://retailmenot.github.io/shield/ to both handle versioning and our microservices. This lets you even entirely swap out the microservice between versions!
- smizell 9y agoI personally like to think about managing change well rather than versioning an entire API. In this, API designers provide instructions on how APIs will evolve over time, how features will be deprecated, and how this process will be communicated to API consumers. Change management also includes recommendations for client developers on how to evolve along with the API and build clients that don't fail when something as small as a JSON property is absent or added. You don't necessarily need version numbers to do this. An API version says to me that one day the entire API may introduce a major change that is separate from the current API. If you plan to never introduce a major change like this, you may not need a "/v1" in the URL. Kudos to the author for putting together a nice article :)
- nradov 9y agoNever say never. It's generally impossible to predict how an API will have to evolve over time in response to changing requirements.
- smizell 9y agoThat's true. However, I think this falls under YAGNI [0]. If you happen to get to a point where you have to change your an entire API rather than evolve what you have, you should consider it a new, separate API rather than instruct all client developers to plan for it up front. Plan for evolvability and handling changing requirements up front. [0] https://martinfowler.com/bliki/Yagni.html https://martinfowler.com/bliki/Yagni.html
- eridius 9y ago> Header versioning in the wild is primarily done via a customized Accept HTTP header. Is this true? I haven't looked at very many web APIs, but I haven't run across anyone using Accept in this way, and it strikes me as a pretty bad mis-use of that header.
- greencurry43 9y agoI've seen some use a versioned media typed like GitHub [0] or add a version parameter like "vnd.example-com.foo+json; version=1.0" [1]. People may use this version for the entire API or for versions of that specific resource as well. [0] https://developer.github.com/v3/media/ https://developer.github.com/v3/media/ [1] http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http http://blog.steveklabnik.com/posts/2011-07-03-nobody-underst...
- Dirlewanger 9y agoHow is it a bad misuse? It's used to specify the MIME type. Why is it so much of a stretch to also specify which version of the MIME type to use? I personally think this is the cleanest and most unobtrusive way to version an API. I'd rather change that than have to change the URL all the the time. Not to mention that /api/vX/... is just ugly.
- eridius 9y agoThe Accept header specifies the acceptable MIME types for the response. It doesn't specify how the request itself is supposed to be interpreted. Picking between alternate MIME types with the Accept header is still supposed to give you the same content, just in different representations. The proper solution here is to just define a new header, like X-API-Version (note: the article picked a header API-Version, but since this is a non-standard vendor-specific header it should be prefixed with X-).
- gnaritas 9y agoA different version is just a different representation of the same resource, the Accept header is the correct way to do it.
- loup-vaillant 9y agoGreat, I was wondering how to version my library once I reach 1.0. > 3: RESTful. It should speak the language of HTTP verbs: GET, PUT, POST, PATCH, UPDATE, DELETE. Oh, it was talking about web interfaces. Oh well.
- deleted 9y ago[deleted]
- tracker1 9y agoIs there a reason you can't use semver? Or was this just a quippy remark?
- loup-vaillant 9y agoThis article isn't even about semantic versioning. It is exclusively about web API design and versioning, and the web doesn't interest me. API used to mean headers, types, Java interfaces… stuff programmers use to link code together. And now the web is hijacking the term to a point where regular APIs can't even be named. I mean, can web developers even think about this kind of API now? I bet this explains part of the microservice madness we've seen lately. If the only kind of API you know goes through a web server, of course you'd use not-so-micro-services to modularise your application.
- tracker1 9y agoMy point was, that if it came to versioning API library versions, then "just use semver" would seem to be the most appropriate answer. I understand the conflation between "web api" and "api" here, I feel the same when "Microsoft SQL Server" is referred to as simple "SQL Server"
- loup-vaillant 9y agoI do intend to "just use semver". I was just hoping this article would give me something more.
- SamUK96 9y ago> 3: RESTful. It should speak the language of HTTP verbs: GET, PUT, POST, PATCH, UPDATE, DELETE. I know it's slightly off-topic, but when one of the early points of an article contains an incorrect piece of information, that really makes me not want to read on further Restful API's are a recent "fad" that, even though_do_ have their place in mainly basic CRUD apps, RPC still reigns very popular. See [1] for a pretty good explanation of REST-styled vs RPC-styled APIs. [1] https://www.linkedin.com/pulse/rest-vs-rpc-soa-showdown-joshua-hartman https://www.linkedin.com/pulse/rest-vs-rpc-soa-showdown-josh...
- tracker1 9y agoI've rarely seen a "pure" API that wasn't also very simplistic... most composite APIs of any complexity have things that work better as an RPC request (via HTTP POST) as well as other more restful endpoints.
- jcrites 9y agoWhat I've learned about API versioning: 1. Versioning an entire web service or RPC API is relatively expensive in terms of engineering resources. Wherever possible, avoid having more than one version of an API, in favor of a single API version that evolves gradually in backwards-compatible ways. Having multiple API versions is a recourse to employ when you've realized that the original API isn't ideal, and you need to break compatibility to get it right. Strive to avoid this. 2. Establish ground rules with your clients about what kind of changes will be considered backward-compatible: what kind of changes they are expected to tolerate. For example, adding new fields to existing structures is something that clients should tolerate from the service. Pay careful attention to enumerations, sum types, and exceptions. 3. Version upgrades are expensive. Upgrading from one API version to another typically requires engineering time from every single client application. Any time that clients spend on version upgrades is time taken away from more useful activities. The total cost of a version upgrade grows in proportion to the number of clients. Don't gratuitously release new API major versions; strive to minimize them. 4. As a service provider, it often seems convenient to just release a new API version rather than put in the hard work to provide a feature in a backward-compatible way. (I see this more often in libraries than in services, where it's difficult to know 100% for sure that your change to the library won't break anyone.) Resist this temptation. Just because the upgrade costs your clients pay are hidden from you doesn't mean they don't exist. Version upgrades are disruptive and frustrating. 5. It's sometimes possible to implement backwards-incompatible changes through a series of smaller backwards-compatible or non-disruptive changes. For example, say that you want to rename an API element -- normally this would be a backward-incompatible change. However, you can break the change up into multiple steps: first, add the new element with the desired name and announce that the old name is deprecated. Next, work with your clients to upgrade them all to the new name. Once clients have migrated, you can remove the old name without impact. (How hard this is and how long it takes depends on the situation; it varies from relatively easy to almost impossible. A key factor is your clients - the more you can coordinate with and influence your clients, the easier this is.) 6. Releasing changes in backward-compatible or non-impacting ways is especially useful because it reduces the need for a service provider and client to directly coordinate. The service provider can announce a feature or change and clients can update to account for it on their own timeline. The service provider can then implement any backward-incompatible steps once all clients are ready. 7. When running any kind of migration, such as renaming an element or moving clients from one major version to another, it's helpful to have statistics about what people are using. If your V1 API isn't getting any traffic because users have moved to V2, or alternatively if it's continuing to receive traffic, then that's important to know if you're planning to deprecate it. 8. Versioning can usually be added after-the-fact if it's needed. It's often not necessary to worry about it too much up-front. I have yet to run into a situation where the lack of a version field caused a problem (because its absence can be interpreted as Version=1). 9. Most of what I said above matters when you have a large number of clients, or when you have clients that are outside your influence. This is big company, big-usage thinking. If you're e.g. a small company building services for your own consumption, don't worry about API versioning until you begin to notice friction. Instead, focus on making changes in backwards-compatible ways. If you need to make a backward-incompatible change, then talk to the people whose software will be affected. There might be an easier way to manage the change than maintaining two parallel API versions.
- deleted 9y ago[deleted]
- pixelcort 9y agoHow about systems like GraphQL where the client specifies the attributes to be returned? To deprecate an attribute, just mark it as deprecated and wait until no client is requesting that field.
- gnaritas 9y agoYou'll be waiting possible forever, clients tend to not change until forced to.
- Moru 9y agoAnd why should they? They probably hired some developer to make the application and this developer might not be available any more. Are you forcing the user to change or are you liberating the user to move to a new company with similar services? If you want your customer to stay with you, make sure it is as frictionless as possible to stay with your service. Of course, if the change is because of security you have to be more clear about it but change just for change sake is dangerous.
- gnaritas 9y agoOh I agree, you should support them as long as you reasonably can if their business justifies the expense and complexity of maintaining multiple versions. At some point it eventually won't, and that's when you force them to upgrade or lose access.
- pluma 9y ago> just No. Marking something as deprecated helps during development. It doesn't help if it's a bunch of deployed code running in production that nobody has the time to maintain for third-party changes. That code will keep making those requests until they fail. GraphQL inherently doesn't address versioning. You'd have to version each attribute and query separately by prefixing or suffixing their names. But then you'd lose the succinctness GraphQL users love it for. The official answer is "Just add new attributes" but that means you're stuck coming up with new names for slightly different versions of existing attributes (or queries). If you try to "be bold" and make things up as you go along, your API will be littered with the equivalent of `2017-06-09_docs_final_v2_new_copy.pdf` if you take backwards compatibility serious. Seeing how Relay is slowly turning GraphQL APIs into RPC APIs ("client sends complex query blob with parameters to server" has become "client sends query ID with parameters to server", server-side GraphQL is now an implementation detail only relevant during development), maybe the right question to ask is: How do I version an RPC API?
- armandososa 9y agoOfftopic: I was amazed by the quality of the illustrations and was pleasantly surprised to find that the woman who makes the illustrations (https://twitter.com/annieruygt https://twitter.com/annieruygt) is listed on the company's about page.
- mrkurt 9y agoAnnie's amazing!
- deleted 9y ago[deleted]
- faitswulff 9y agoRandom thought, but would it make sense to give each client a unique base path for URL endpoints and let them instantiate new endpoints with different versions? Ex: mysite.com/api/UUID_for_api_v1/cars/1 and mysite.com/api/UUID_for_api_v2/automobiles/1
- caffeinewriter 9y agoI was just thinking about this. Instead of versioning with an `Accept` header, or with a URL version, why not version off of an upgradable API key?
- TimWolla 9y agoFacebook does this partly: Each “Application” has an associated API version which is the earliest API version that can be accessed (and IIRC the one used implicitly when no version is given inside the route).
- paulddraper 9y ago> RESTful. It should speak the language of HTTP verbs: GET, PUT, POST, PATCH, UPDATE, DELETE What in the heck is UPDATE? It sounds like a non-technical person found out REST had CRUD.
- itcmcgrath 9y agoIt's the verb you use when you want to receive a 400 with "BAD Request - Invalid verb"
- abraae 9y agoBut failed to also add INSERT.
- jalfresi 9y agoYou can define your own HTTP verbs if you want
- jholman 9y agoThere's nothing RESTful about using more than 2 HTTP verbs (GET for SAFE endpoints, and POST for UNSAFE ones). (Mind you, REST does require that you obey the semantics of HTTP for the verbs that you do use, standard or non-standard). And then the API endpoints illustrated don't obey HATEOS. So there's nothing RESTful about the article either.
- cdevs 9y agoWe do this at my job, what better way to screw over API users than to make them rewrite their integration every 2 years
- smnscu 9y agoWrt your users, I think regardless of what scheme you use for API versioning it's a good idea to build libraries to access it – if you can afford it. [Go :), ]JS, PHP, C#, Java, Python, and Ruby, and you have most of the market covered. Then you can use semver for the libraries and the user doesn't care about how the actual endpoints look like – as an added bonus you can use more efficient encodings. Example from a past life: https://github.com/lavab/api-client-js https://github.com/lavab/api-client-js
- defanor 9y agoThe title is a bit misleading: the article is about HTTP-based API versioning, not just any kind of API. A more generic approach that would fit such a title is "semantic versioning" [1], which covers different questions. [1] http://semver.org/ http://semver.org/
- wst_ 9y agoYeah. People should stop saying API for a HTTP API. This is an application programming interface. Any application.
- iask 9y agoI go by this - breaking changes require versioning. And once I version, there is no time spent on backward compatibility. Time is money.
- and0 9y agoHas anyone ever designed an API with subversioning on single calls? So you'd have: service.com/api/v1/do_thing/v2/param That way, you wouldn't have to try to snap new features to entire numbered revisions,and could open new parameters / results up to clients who are interested in the meantime. Seems like it would be the best of both worlds, but I've never had to design a serious API.
- novaleaf 9y agoarticle doesn't include "module", which I think is a big mistake, as it assumes a monolithic api for the entire business. My opinion for a better structure: domain.tld/api/module/version/