interagent / http-api-design

HTTP API design guide extracted from work on the Heroku Platform API
https://geemus.gitbooks.io/http-api-design/content/en/
Other
13.69k stars 1.07k forks source link

Explain why to version in accept header #8

Open justincy opened 10 years ago

justincy commented 10 years ago

A great example is Version with Accepts header. I wouldn't say it's obvious why putting the version in the accept header is valuable as opposed to versioning in the URL.

creynders commented 10 years ago

:+1: Most of it is quite clear though. The most puzzling (and counter-intuitive) one to me is "Provide full resources where available". I'd think it's best NOT to include the full resource (for responses with collections for instance), but provide a url to the full resource instead and/or allow for some kind of requested-fields mechanism.

troy commented 10 years ago

Seconding @justincy's suggestion, or at least discussing the trade-offs or linking to justification for the opinions. The comments on this GitHub issue show why: reasonable people disagree about some items, so understanding the reason Heroku picked one is helpful.

Using a MIME type attribute for the version (rather than the MIME type string or the request path) is a good example. For background, see Steve Klabnik's comments. When I tried to use MIME type attributes for a new API in early 2014, almost no Web frameworks or HTTP client libraries supported it. Some frameworks made it almost impossible to route based on, as is required to offer 2 versions concurrently.

Obviously every situation and implementation is different, and this isn't an API Bible, it's what the authors chose. That said, since it's been extracted into a standalone document, it's likely to be referred to alone. Even one link to the best existing discussion/supporting justification might help readers reach the best decision for their situation.

geemus commented 10 years ago

Thanks for the feedback. I've been a bit buried after some time away at a couple conferences, but I'll definitely try to expand on there in the near future.

troy commented 10 years ago

My pleasure. I love the idea behind the doc and it's something that many people sorely need. Gave it a little much-justified love in https://twitter.com/papertrailapp/status/470222333517369344.

If you want a hand with a PR with some notes or links, let me know. I didn't want to do that un-prompted because I figured you'd have a style in mind, aside from opinions on the actual content :)

geemus commented 10 years ago

@troy thanks, glad you like it. I definitely spent a lot of time considering what was out there and what we had to reach some of these conclusions. Time (and discussion) will tell how universal some of the choices end up being. I'm certainly happy to receive any feedback/input you have as it is likely to help me flesh it out and inform me of oversights I have from focusing mostly on our API use case. I'll probably compile my thoughts and yours ultimately, so notes might be most helpful (I would hate for you to spend too much time on actual verbiage that I am likely to tweak/transform if that makes sense). Definitely happy to discuss other parts too if you are interested, though breaking out stuff other than version to separate issues seems good in terms of not letting this thread get too out of control.

@justincy - thanks for starting the discussion. I'm going to rename this issue to focus it around versioning, but if there are other things you would like to see clarification around please open individual issues so we can contain/moderate threads a bit better and just to keep things manageable for the sake of my sanity. Thanks!

@creynders - I split off the full resource discussion in to #9 to keep things manageable and will try to add more details in the near future. Thanks!

geemus commented 10 years ago

@justincy @troy

As a simplifying assumption I like to try and separate the concerns of different parts of HTTP, which I have found provides a useful foundation to build things upon. It looks roughly like this:

So, given this. You might change host to ask for something from someone else, or to change from staging to production environments. The object that you are asking for is based on the path, which we keep constant as a version change would not mean that you are now referring to a different object (though the representation may have changed). Instead we lean on headers to pass the metadata of what representation we prefer.

This is likely an oversimplification, but I think it makes it a bit easier to talk about and fits in well with my understanding of the intention of related specs/HTTP bits. Hope that helps, but do let me know if I can better clarify, if you have questions and if you have suggestions on how we can clarify this in the docs. Thanks!

@steveklabnik if you have further comments around this that would be awesome. we had discussions at the time that were influential in this initial choice and the registry of our mime-type which I don't want to misrepresent. Thanks!

steveklabnik commented 10 years ago

This is gonna kinda be a drive-by comment for now, because I'm on a flight layover. Sorry. :( I'll try to expand more later.

Basically, my opinion is split in several ways:

Are you asking, "Given no other constraints, what's the right way to do it?" My answer to that question is "versioning is an antipattern. Don't version your API. And use hypermedia."

Are you asking, "I'm building a Rails-style REST API. What's the right way to do it?" My answer to that question is "Versioning in the URL is better."

Are you asking "Is versioning in the header wrong?" My answer to that question is, "not exactly, though I do think that it has lots of downside with very little upside, and there are actually good arguments that this is wrong."

Sorry I don't have time for more than that. :/

@troy also, see the header part of that post:

I specifically don't agree that versioning the media type is how to properly version APIs. Hypermedia APIs should not actually use explicit versioning, but I'd rather see a version in the URI with HATEOAS than no HATEOAS and versioned media types.

I guess I coulda just copy/pasted that...

geemus commented 10 years ago

If I totally had my way, I'm somewhat inclined to put the version in the host (ie it is part of who you are interacting with). But that also has downsides, as do all of the solutions really.

geemus commented 10 years ago

@steveklabnik thanks for taking a moment to chime in.

h2non commented 9 years ago

I had the same question. I'd prefer don't becoming toxic at URL level with the version. Maybe for some particular scenarios could be a reasonable solution, for instance, if you're behind a reverse HTTP proxy or for public APIs (which is particularly more simple to use and differ between versions, as well for end developers).

My vote is to define a new HTTP header, such as Version or X-Version for that purpose.

geemus commented 9 years ago

Yeah, I think the clearer separation provided by having it in headers is nice, but that the format of accept can be a bit cumbersome. Having it more isolated could certainly still keep it in a clear location but avoid some of those pains. If coupled with a query override (kind of like method override) it also provides a path toward specifying in path (via query) where header is inconvenient or not possible.

crazytonyi commented 9 years ago

Isn't this what the Pragma header was originally intended for?

The Pragma general-header field is used to include implementation- specific directives that might apply to any recipient along the request/response chain. All pragma directives specify optional behavior from the viewpoint of the protocol; however, some systems MAY require that behavior be consistent with the directives.

h2non commented 9 years ago

I don't have a clear about if Pragma header should be used for this purpose. According to the W3C spec, that header could be filled with custom values to be used to include implementation specific directives, but historically and empirically no ones places this information in that header, maybe just because it was misunderstood.

I believe that extending HTTP headers with non standards fields is not generally speaking a bad practice, and from the client point of view I think it's simple and explicit defining a custom header for that purpose, and also from my perspective is less error prone.

However, I've to say that multiple alternatives sounds reasonable. I just want to tend to the better one.

geemus commented 9 years ago

I suppose Pragma could work, given it's definition, but I've not really seen it used for anything other than disabling caching (and even then, there is a nicer/more specific way to do this in http 1.1). Still, inventing new things has it's own downsides. But I guess to some extent having this as a header all it's own certainly simplifies things vs reusing something else. ie with pragma you might have to stack it on top of some other directives and then you get back to the pain of building/parsing that we currently have with accept. Not that this is, by any means insurmountable (we use accept presently), but that also doesn't mean there couldn't be a clearer, simpler way.

mxmlnglt commented 5 years ago

As the link in the original comment doesn't work anymore, here's the file https://github.com/interagent/http-api-design/blob/master/en/foundations/require-versioning-in-the-accepts-header.md or https://geemus.gitbooks.io/http-api-design/content/en/foundations/require-versioning-in-the-accepts-header.html