-
Notifications
You must be signed in to change notification settings - Fork 872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarify the versioning strategy for APIs #406
Comments
This seems like it falls outside the scope of json-api. Any of those options seem fine,
|
Why is it outside of the scope? It's quite an important aspect of API design. |
By the way, at least one technique (using Content-Type header) conflicts with existing json-api spec which requires using specific "Content-Type: application/vnd.api+json" header value. |
@extesy I have an API where the clients use Hope that helps. |
While it is up to the implementation to decide how to communicate the API version (in URL, headers or elsewhere), would it be reasonable to include a recommendation to use a specific strategy for versioning APIs? IMHO, semantic versioning is a good candidate for this. |
It would seem to make a lot of sense to come up with a common convention for advertising a specific JSON API version. I mean why not? Doesn't have to be decided before 1.0 though.
@dkubb That doesn't look very safe, since it's not out of the realm of possibility that JSON API might want to use the same parameter in the future. |
And that's one of the reason why I proposed to have the spec define the strategy. Otherwise, some custom solutions conflict either with existing spec already, or might conflict with some future version. To avoid these conflicts, spec should define the only one "blessed" approach. |
I'm for adding a reccomendation, but we don't need this in the actual text. Will post more later. |
It seems like each version of an API should be on its own in its own directory. Thus any changes in format won't affect the previous at all. Then you can have something like this:
The JSON-API base exists in the folder and thus the prefix ( |
@ColtonProvias That's just one of the ways to do api versioning, but not the only one. There are even solutions other than path and header, for example see how Stripe is doing it: https://stripe.com/docs/upgrades |
True. Even though it is deprecated, maybe a x- header would do the trick.
The question still exists of if this should be part of the standard or if it is outside of the spec. Some sites probably have old versioning schemes in place that if we specify a particular scheme, it may conflict. Keeping backwards compatibility then would be a major issue. |
Would it be an idea to have either a namespaced version parameter to avoid collisions with the jsaonapi spec? So something like this? Not certain if the dot is allowed as part of a parameter name though.
|
@visualasparagus A dot is indeed allowable as part of a token as per RFC 2616 §2.2 I'd argue in favor of namespaced versions for the same reason. With the exception of reversing the order:
More generally, namespaces SHOULD be used for any media type parameters. Including those used by json-api. |
I needed this information from the jsonapi spec but it's not in the jsonapi spec, is there a reason? I really really like using the key/value store in the Accept header's parameters. |
@ceykooo Your solution looks best to me. But than I noted this in the spec:
So I ended up with something like this:
|
please see / should fix json-api#867 json-api#851 partially fixes json-api#406 ---> see my following answer in json-api#867 regarding versioning
is this the recommended way of api versioning now? |
@Perni1984 no, that's the version of JSON API itself. Which is what media type parameters are for: to parameterize the media type. I personally recommend "versioning" through URLs. |
@steveklabnik: thanks for the explanation, I think I did not communicate well. I understood that the spec says to send the following header to announce JSON API compatability:
What I don't understand is how I can add the version of our companies API, as the spec explicitely states to _NOT add any media type parameter_ (see @Art4 comment above), if I read it correctly. So I am not really sure that this is a valid way according to the specs:
If that header is valid according to the spec, perfectly fine, I have my solution. But if I am not missing something I really would like to version the company api through accept headers for various reasons and avoid using URLs for versioning. |
This goes against the RFCs, though, and if we want JSON API to get through the IETF, we'll have to abide by them. |
would it be possible to send multiple accept headers? E.g.
|
@Perni1984 My comment has already multiple accept headers
Afaik this fits the spec, because it specifies "the media type there at least once without any media type parameters". |
@Art4: Ah, now I got it, thanks! @Art4 & @steveklabnik: What about specifity? Wouldn't it be better to put the more specific accept header on top? E.g.
|
I still think this isn't correctly using the media type. You're trying to request two media types, and always getting one back, but using parameters from the other media type, the one you're not using. |
@steveklabnik This is the recommendation at #673, 3rd point from @ethanresnick
|
@Art4 @Perni1984 Just to clarify... The spec says that clients' But, just because we're making it possible for JSON API to add media type parameters in the future, that doesn't mean that the media type can be used now with parameters that aren't part of the JSON API spec (or part of its IANA registration). Using a media type with parameters that it doesn't define is a violation of the general RFCs governing media type use, which was @steveklabnik's point. So, if you want to version your API, my recommendation would also be to use different URIs. If you don't want to use different URIs, you can add another header, like @ColtonProvias suggested. That said, if you don't want to use URIs, I'm really curious why, so please let me know! |
@ethanresnick @steveklabnik: I am still learning REST and Hypermedia, and I am open to improve myself, but basically I don't like the idea of "polluting" the URL with a version number, if there is another clever way of solving the versioning issue and being compliant with all the standards. I also dislike the idea of adding a custom header and then adding this custom header in the Vary statement to be standards compliant and having no problems with caches and therelike - I don't think this is an "universal" solution. I understand that if you want to get the spec through the IETF you have to abide by their rules. So what do you think about putting the company api version in a custom extension media type. E.g.
So I could serve v1 of my company API aswell as v1 of JSON-API when no additional media type parameter is given:
What do you think about it? |
@Perni1984 👍 I really like your approach. Looks very clean. |
I still think that using the media type to version is just plain incorrect. Media type parameters are for the media type, not for the underlying service. |
I understand your point, but in the lack of better suited alternatives for my use case I would prefer this solution. FYI I still consider this option being better suited for my use case than URL versioning or custom headers for the above mentioned reasons. Could you eventually let me know if you see any problems with that not being standards complaint? |
@Art4 I appreciate the thorough rundown. One point about URI based versioning, though, to add to what @steveklabnik said... In REST/HTTP, a resource is a concept, right, like "Today's weather in SF". Then that concept is identified by a URI and takes on different representations over time. So, you can think of URI versioning as legitimately creating new resources if you think of the concepts behind each URI as different. That is, the resource identified by Imagine, for instance, that in v2 you switch your service to accept a different kind of PATCH format, or you switch some parts of v2 to not supporting some methods. In that case, "Part x in v1" and "Part x in v2" actually work differently; it's not just the representation that's changing; and they are distinct concepts that you might (e.g.) want to be able to link to individually. This is what I meant when I said earlier:
So, the downside of approach 3 isn't just violating the media type rules (which come from the IETF in combination with our particular IANA registration), it's that it limits the kinds of across-major-version changes that you can appropriately express semantically. URI based versioning has no such limitations, and doesn't need to be seen as violating REST principles. To your point about URI-based versioning forcing consumers to do weird stuff to keep links up-to-date... the solution there is probably more REST (i.e. more inline links and less client URI construction). JSON API already has limited features for this, and we plan to add more in the future |
Several comments in this thread mention media type "rules" and IANA and RFCs... But I'm curious, where does it actually say that using proprietary parameters with a registered media type is not OK? I'm aware that RFC 6838 requires (or recommends, depending on the tree) that standard parameters for a media type are registered along with the type itself. But nowhere does it prohibit clients and servers from tacking on additional, mutually understood parameters, and neither does HTTP. |
I think it's inherent in, or at least strongly implied by, the definition of a media type parameter. From RFC 2046, which defines what a media type is: "Parameters are modifiers of the media subtype... The set of meaningful parameters depends on the media type and subtype." So, the concept of a parameter whose meaning is determined by an external contract between client and server, rather than by the media type, isn't consistent with the definition of a media type parameter in the first place. Note: RFC 2046 also says that mime implementations are supposed to ignore parameters they don't recognize. That should make it technologically possible to tack on extra parameters, but it doesn't change the fact that doing so makes no sense semantically. |
Thank you @ethanresnick , you said it better than I. |
RFC 2046 doesn't say parameters can only be derived from media type definitions. The quoted passage simply means there are no universal parameters common to all media types. If JSON API wants to prohibit media type parameters, that's obviously its prerogative, but pinning it on the IETF or the IANA registration doesn't seem warranted. For example, the |
@bintoro Maybe I'm missing something, but "[t]he set of meaningful parameters depends on the media type and subtype" seems to mean precisely that "parameters can only be derived from media type definitions". But let's back up a second... suppose a parameter can be used. It would still only be valid for indicating certain types of changes (those that effect the representation only), so we'd have to a recommendation that says: "You may use URI versioning or a media type parameter to indicate breaking changes, except in cases where more than the representation changes; then you must use a URI change." Isn't that just unnecessarily complex compared to simply recommending URI versioning? And what are the odds that people who start off using media type parameters to version really switch to URI versioning when it becomes semantically appropriate to do so? (I'd put those odds very low.) Meanwhile, I still don't really understand the supposed benefits of media type parameter based schemes, other than keeping the URI "clean" looking. Imo, though, that benefit isn't worth the complexity. |
@ethanresnick From my view you always need a mix of URL versioning and media type versioning. URL versioning is the best method for API structure changes. Every API can require such changes, so I'd always add a version to the URI. Of course this means that some resources can be accessed with different URIs in different versions - but this cannot be avoided, if you want to be able to restructure your API later and still want to offer your API consumers the old API and give them time to switch. This is very important, because it takes time for clients to change and otherwise you would have to break all clients which cannot immediately switch, or you would have to extend everything on base of the old API structure, which could result in an illogical structure and cause problems later. On the other side we have different resource representations, which are totally independent from the API structure version. When I request an XML representation I want to get XML, when I request a JSONAPI representation, I want to get JSONAPI. And when I request a special version of this representaion format (which can be JSONAPI in a given version or with special extensions) I want to get it, no matter which API version I use (but of course a new API structure versions can redefine the requirements and drop support for old representation versions). And this is IMHO best done with content negotiation, because this enables the client to accept multiple supported versions at the same time, using the same URI for the request. Also - as mentioned earlier - it would simplify the switch to newer JSONAPI versions for clients and servers (and solve a chicken/egg problem here), because no URL or logical changes are requiered. If clients and servers support the newer version, they can automatically use it. Just my point of view... |
I've been doing HATEOAS API development for the last few years and I had always been on the side of "Version are a part of content negotiation and media representation!" @steveklabnik just blew that out of the water for me though. When I see a change from |
Please take into consideration, that in most cases, company version their API facade through headers or urls like
This allows detecting and changing JSON API semantic version without necessarily changing company's API facade version (even though in most cases should, but we should allow people to make that decision for themselves). And also is very friendly for handling errors. |
Looks like this should be closed per #1020 ? |
@steveklabnik I like using the Accept header for the version of an API endpoint, I agree with this http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http#i_want_my_api_to_be_versioned
Here is a tongue and cheek article on the subject: https://www.troyhunt.com/your-api-versioning-is-wrong-which-is/ and another https://blog.pivotal.io/pivotal-labs/labs/api-versioning The Accept header generally allows media type parameters, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html I'm curious how the JSON API spec concluded without any media type parameters…
|
@pixelhandler if you read the first sentence of that post
|
@pixelhandler Please read the thread, and comment back with any specific questions. The decision on media type parameters is well documented above, I think. |
@steveklabnik ah I see thanks. @ethanresnick yeah reading it thanks. I think that https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html allows for
Could |
Re accept-extension params, from upthread:
|
Was the conclusion here that |
I used to be on the train of doing api versioning in headers. A. Tooling doesn't expect it, welcome to writing your own stuff. |
I'd like to close this, as I think everything that can be said on the topic has been at this point. However, I would like to get an official recommendation onto the recommendations page before closing this. Would anyone want to take a stab at drafting such a rec? |
@ethanresnick , I’d be happy to write this up sometime over the next few days. How’s that sound to you? |
That would be awesome. Thanks James! |
Actually, the only confusing part of JSON:API for us was to see that there's not even a suggestion about versioning, but instead a rather surprisingly "rich" thread about it, and yet no resolution. 🙃 We went for the double accept header like
By my experience that's accepted as by the specs by any implementation of JSON:API. 🤗 |
URI VersioningVersioning in the URI means that I'm referring to two different resources, because having a different path does mean that it is a different resource. In practice, you will probably have some resources that are identical to the previous version when you make a new version this way. That means your API isn't guaranteeing that they are the same resource, just that the new resource in question behaves the same way as the old resource. You can trust that they are interchangeable in that sense. For example, Header VersioningVersioning in headers indicates I am retrieving the same resource with a different representation. When a new version is introduced, the path remains the same (which means it is the same resource), but I can now request a new version of the resource if I'd like to do so. It's less visible, for sure, but it gives the client the guarantee that I'm accessing the same resource, just at a different representation version. In practice, I don't see much difference between the tradeoffs here since you are probably going to give your clients the guarantees they need anyways. I think there is also a body of thought that suggests having a different media type for each of your resources, in which case header versioning actually makes a lot more sense. In that case you would be requesting a specific media type at a specific version. I have never seen this in practice. Brief note about semver and restful APIsI also want to make a brief note that semantic versioning introduces more structure than is necessary for API versioning from a practical perspective. Compatible changes (minor and patch) don't require a version change in practice since clients can keep using the representations just fine without changing their expectations. Incompatible changes demand a new version, otherwise you are putting clients at a high risk of breaking if they don't make changes to their behavior to accommodate. |
@jamesplease Do I assume correctly that you haven't had time to do so? |
I think it would be useful to standardize the approach to versioning the API. For example, when I want to change the request or response format for some API endpoint, which is backwards incompatible. How should the client tell which API version to use?
There are many different approaches to this: put version in url like /api/1/action or /api/v1/action or in the header, like "Content-Type: application/vnd.company+json.v1" or "X-API-Version: 1".
How about explicitly including the strategy to versioning API in this specification?
The text was updated successfully, but these errors were encountered: