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

Open
extesy opened this Issue Mar 6, 2015 · 60 comments

Comments

Projects
None yet
@extesy

extesy commented Mar 6, 2015

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?

@tkellen

This comment has been minimized.

Show comment
Hide comment
@tkellen

tkellen Mar 6, 2015

Member

This seems like it falls outside the scope of json-api. Any of those options seem fine,
though!
On Mar 5, 2015 7:55 PM, "Oleg Anashkin" notifications@github.com wrote:

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?


Reply to this email directly or view it on GitHub
#406.

Member

tkellen commented Mar 6, 2015

This seems like it falls outside the scope of json-api. Any of those options seem fine,
though!
On Mar 5, 2015 7:55 PM, "Oleg Anashkin" notifications@github.com wrote:

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?


Reply to this email directly or view it on GitHub
#406.

@extesy

This comment has been minimized.

Show comment
Hide comment
@extesy

extesy Mar 6, 2015

Why is it outside of the scope? It's quite an important aspect of API design.

extesy commented Mar 6, 2015

Why is it outside of the scope? It's quite an important aspect of API design.

@extesy

This comment has been minimized.

Show comment
Hide comment
@extesy

extesy Mar 6, 2015

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 commented Mar 6, 2015

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.

@dkubb

This comment has been minimized.

Show comment
Hide comment
@dkubb

dkubb Mar 6, 2015

@extesy I have an API where the clients use Accept: application/vnd.api+json; version=1 to select the api version. The version corresponds to the app's versioning scheme, not necessary json-api's version. If I make a backwards incompatible change, like removing or renaming a relationship, I bump the version up. I've been using versioncake to manage negotiating the proper view template based on the version.

Hope that helps.

dkubb commented Mar 6, 2015

@extesy I have an API where the clients use Accept: application/vnd.api+json; version=1 to select the api version. The version corresponds to the app's versioning scheme, not necessary json-api's version. If I make a backwards incompatible change, like removing or renaming a relationship, I bump the version up. I've been using versioncake to manage negotiating the proper view template based on the version.

Hope that helps.

@hhware

This comment has been minimized.

Show comment
Hide comment
@hhware

hhware Mar 6, 2015

Contributor

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.

Contributor

hhware commented Mar 6, 2015

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.

@bintoro

This comment has been minimized.

Show comment
Hide comment
@bintoro

bintoro Mar 6, 2015

Contributor

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.

I have an API where the clients use Accept: application/vnd.api+json; version=1 to select the api version. The version corresponds to the app's versioning scheme, not necessary json-api's version.

@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.

Contributor

bintoro commented Mar 6, 2015

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.

I have an API where the clients use Accept: application/vnd.api+json; version=1 to select the api version. The version corresponds to the app's versioning scheme, not necessary json-api's version.

@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.

@extesy

This comment has been minimized.

Show comment
Hide comment
@extesy

extesy Mar 6, 2015

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.

extesy commented Mar 6, 2015

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.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Mar 9, 2015

Contributor

I'm for adding a reccomendation, but we don't need this in the actual text. Will post more later.

Contributor

steveklabnik commented Mar 9, 2015

I'm for adding a reccomendation, but we don't need this in the actual text. Will post more later.

@ColtonProvias

This comment has been minimized.

Show comment
Hide comment
@ColtonProvias

ColtonProvias Mar 10, 2015

Contributor

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:

/api/1/users returns an XML resource
/api/2/users returns a binary resource from that time when you got drunk and thought this would be best.
/api/3/users returns a JSON-API resource.

The JSON-API base exists in the folder and thus the prefix (/api/3) falls outside of the spec. Essentially JSON-API starts at the resource type in the path and covers everything to the right of it. Everything left of the resource type is your own design. If you want your API to be https://catfacts.io/annoy/some/users, go right ahead.

Contributor

ColtonProvias commented Mar 10, 2015

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:

/api/1/users returns an XML resource
/api/2/users returns a binary resource from that time when you got drunk and thought this would be best.
/api/3/users returns a JSON-API resource.

The JSON-API base exists in the folder and thus the prefix (/api/3) falls outside of the spec. Essentially JSON-API starts at the resource type in the path and covers everything to the right of it. Everything left of the resource type is your own design. If you want your API to be https://catfacts.io/annoy/some/users, go right ahead.

@extesy

This comment has been minimized.

Show comment
Hide comment
@extesy

extesy Mar 10, 2015

@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

extesy commented Mar 10, 2015

@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

@ColtonProvias

This comment has been minimized.

Show comment
Hide comment
@ColtonProvias

ColtonProvias Mar 10, 2015

Contributor

True. Even though it is deprecated, maybe a x- header would do the trick.

X-API-Version: 1

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.

Contributor

ColtonProvias commented Mar 10, 2015

True. Even though it is deprecated, maybe a x- header would do the trick.

X-API-Version: 1

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.

@visualasparagus

This comment has been minimized.

Show comment
Hide comment
@visualasparagus

visualasparagus Mar 22, 2015

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.

Accept: application/vnd.api+json;version.com.my.api=1.1

visualasparagus commented Mar 22, 2015

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.

Accept: application/vnd.api+json;version.com.my.api=1.1
@ceyko

This comment has been minimized.

Show comment
Hide comment
@ceyko

ceyko Mar 28, 2015

@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:

Accept: application/vnd.api+json; com.example.api.version=1.1

More generally, namespaces SHOULD be used for any media type parameters. Including those used by json-api.

ceyko commented Mar 28, 2015

@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:

Accept: application/vnd.api+json; com.example.api.version=1.1

More generally, namespaces SHOULD be used for any media type parameters. Including those used by json-api.

@krainboltgreene

This comment has been minimized.

Show comment
Hide comment
@krainboltgreene

krainboltgreene May 1, 2015

Contributor

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.

Contributor

krainboltgreene commented May 1, 2015

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.

@Art4

This comment has been minimized.

Show comment
Hide comment
@Art4

Art4 Jun 16, 2015

Contributor

@ceykooo Your solution looks best to me. But than I noted this in the spec:

Clients that include the JSON API media type in their Accept header MUST specify the media type there at least once without any media type parameters.

So I ended up with something like this:

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1

Contributor

Art4 commented Jun 16, 2015

@ceykooo Your solution looks best to me. But than I noted this in the spec:

Clients that include the JSON API media type in their Accept header MUST specify the media type there at least once without any media type parameters.

So I ended up with something like this:

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1

sebilasse pushed a commit to redaktor/json-api that referenced this issue Sep 14, 2015

redaktor
added JSON schemas 🔍
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
@Perni1984

This comment has been minimized.

Show comment
Hide comment
@Perni1984

Perni1984 Feb 17, 2016

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1

is this the recommended way of api versioning now?

Perni1984 commented Feb 17, 2016

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1

is this the recommended way of api versioning now?

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Feb 17, 2016

Contributor

@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.

Contributor

steveklabnik commented Feb 17, 2016

@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.

@Perni1984

This comment has been minimized.

Show comment
Hide comment
@Perni1984

Perni1984 Feb 17, 2016

@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:

Accept: application/vnd.api+json

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:

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1

If that header is valid according to the spec, perfectly fine, I have my solution. But if I am not missing something com.example.api.version=1.1 is an media type parameter, so the above statement violates the spec. Correct?

I really would like to version the company api through accept headers for various reasons and avoid using URLs for versioning.

Perni1984 commented Feb 17, 2016

@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:

Accept: application/vnd.api+json

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:

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1

If that header is valid according to the spec, perfectly fine, I have my solution. But if I am not missing something com.example.api.version=1.1 is an media type parameter, so the above statement violates the spec. Correct?

I really would like to version the company api through accept headers for various reasons and avoid using URLs for versioning.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Feb 17, 2016

Contributor

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.

Contributor

steveklabnik commented Feb 17, 2016

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.

@Perni1984

This comment has been minimized.

Show comment
Hide comment
@Perni1984

Perni1984 Feb 17, 2016

would it be possible to send multiple accept headers? E.g.

Accept: application/vnd.api+json;
Accept: application/vnd.mycompany.v1+json

Perni1984 commented Feb 17, 2016

would it be possible to send multiple accept headers? E.g.

Accept: application/vnd.api+json;
Accept: application/vnd.mycompany.v1+json
@Art4

This comment has been minimized.

Show comment
Hide comment
@Art4

Art4 Feb 17, 2016

Contributor

@Perni1984 My comment has already multiple accept headers

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1
// or
Accept: application/vnd.api+json
Accept: application/vnd.api+json; com.example.api.version=1.1

Afaik this fits the spec, because it specifies "the media type there at least once without any media type parameters".

Contributor

Art4 commented Feb 17, 2016

@Perni1984 My comment has already multiple accept headers

Accept: application/vnd.api+json, application/vnd.api+json; com.example.api.version=1.1
// or
Accept: application/vnd.api+json
Accept: application/vnd.api+json; com.example.api.version=1.1

Afaik this fits the spec, because it specifies "the media type there at least once without any media type parameters".

@Perni1984

This comment has been minimized.

Show comment
Hide comment
@Perni1984

Perni1984 Feb 17, 2016

@Art4: Ah, now I got it, thanks!
@steveklabnik: Well now I think it is perfectly valid inside the spec - you agree?

@Art4 & @steveklabnik: What about specifity? Wouldn't it be better to put the more specific accept header on top? E.g.

Accept: application/vnd.api+json; com.example.api.version=1.1
Accept: application/vnd.api+json

Perni1984 commented Feb 17, 2016

@Art4: Ah, now I got it, thanks!
@steveklabnik: Well now I think it is perfectly valid inside the spec - you agree?

@Art4 & @steveklabnik: What about specifity? Wouldn't it be better to put the more specific accept header on top? E.g.

Accept: application/vnd.api+json; com.example.api.version=1.1
Accept: application/vnd.api+json
@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Feb 17, 2016

Contributor

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.

Contributor

steveklabnik commented Feb 17, 2016

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.

@Art4

This comment has been minimized.

Show comment
Hide comment
@Art4

Art4 Feb 17, 2016

Contributor

@steveklabnik This is the recommendation at #673, 3rd point from @ethanresnick

It accounts for the fact that the Accept header can take multiple values, e.g. Accept: application/vnd.api+json;ext=bulk, application/vnd.api+json, and that the header is only problematic if every JSON-API option it lists contains parameters.

Contributor

Art4 commented Feb 17, 2016

@steveklabnik This is the recommendation at #673, 3rd point from @ethanresnick

It accounts for the fact that the Accept header can take multiple values, e.g. Accept: application/vnd.api+json;ext=bulk, application/vnd.api+json, and that the header is only problematic if every JSON-API option it lists contains parameters.

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Feb 17, 2016

Member

@Art4 @Perni1984 Just to clarify...

The spec says that clients' Accept header must contain at least one unparameterized instance of the media type because, if JSON API defines a parameter in the future, old clients will still need to handle (i.e. Accept) the unparameterized version.

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!

Member

ethanresnick commented Feb 17, 2016

@Art4 @Perni1984 Just to clarify...

The spec says that clients' Accept header must contain at least one unparameterized instance of the media type because, if JSON API defines a parameter in the future, old clients will still need to handle (i.e. Accept) the unparameterized version.

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!

@Perni1984

This comment has been minimized.

Show comment
Hide comment
@Perni1984

Perni1984 Feb 18, 2016

@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.

Accept: application/vnd.api+json; ext=com.example.api.version+2.1.1

So I could serve v1 of my company API aswell as v1 of JSON-API when no additional media type parameter is given:

Accept: application/vnd.api+json;

What do you think about it?

Perni1984 commented Feb 18, 2016

@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.

Accept: application/vnd.api+json; ext=com.example.api.version+2.1.1

So I could serve v1 of my company API aswell as v1 of JSON-API when no additional media type parameter is given:

Accept: application/vnd.api+json;

What do you think about it?

@masterspambot

This comment has been minimized.

Show comment
Hide comment
@masterspambot

masterspambot Feb 18, 2016

Contributor

@Perni1984 👍 I really like your approach. Looks very clean.

Contributor

masterspambot commented Feb 18, 2016

@Perni1984 👍 I really like your approach. Looks very clean.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Feb 18, 2016

Contributor

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.

Contributor

steveklabnik commented Feb 18, 2016

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.

@Perni1984

This comment has been minimized.

Show comment
Hide comment
@Perni1984

Perni1984 Feb 18, 2016

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?

Perni1984 commented Feb 18, 2016

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?

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Mar 8, 2016

Member

@Art4 If I understand you correctly, you're saying that, during development, minor versions act like major versions do during production (i.e., they may contain breaking changes). That's totally fair, but my advice in that case would be to treat them just like how I described treating major versions earlier. That is, I'd still recommend putting them in the URI, for the reasons I gave in that earlier post. I still don't understand why that suggestion is problematic either. What am I missing?

As I also mentioned in my earlier post, using a media type parameter like ext might be legal, but that would only be true once the parameter was standardized and added to JSON API's media type registration. The ext parameter has not been standardized (it's still marked "experimental") and, in fact, is likely to go away. So, if you do stick with a media type parameter, I wouldn't use that parameter in particular. Instead, I'd wait until #957 is resolved, and use the profile parameter, assuming that makes it in to the base spec.

To @bintoro's point... there is a second type of parameter that the Accept header allows, called an extension parameter, which is distinct from media type parameters. So you could probably use that instead of using the URI or waiting on JSON API to add a suitable media type parameter. However, I've never actually seen anyone use an Accept extension parameter in real life, so I would expect interoperability issues, with some parsers mistakenly treating them as media type parameters or ignoring them entirely.

Member

ethanresnick commented Mar 8, 2016

@Art4 If I understand you correctly, you're saying that, during development, minor versions act like major versions do during production (i.e., they may contain breaking changes). That's totally fair, but my advice in that case would be to treat them just like how I described treating major versions earlier. That is, I'd still recommend putting them in the URI, for the reasons I gave in that earlier post. I still don't understand why that suggestion is problematic either. What am I missing?

As I also mentioned in my earlier post, using a media type parameter like ext might be legal, but that would only be true once the parameter was standardized and added to JSON API's media type registration. The ext parameter has not been standardized (it's still marked "experimental") and, in fact, is likely to go away. So, if you do stick with a media type parameter, I wouldn't use that parameter in particular. Instead, I'd wait until #957 is resolved, and use the profile parameter, assuming that makes it in to the base spec.

To @bintoro's point... there is a second type of parameter that the Accept header allows, called an extension parameter, which is distinct from media type parameters. So you could probably use that instead of using the URI or waiting on JSON API to add a suitable media type parameter. However, I've never actually seen anyone use an Accept extension parameter in real life, so I would expect interoperability issues, with some parsers mistakenly treating them as media type parameters or ignoring them entirely.

@Art4

This comment has been minimized.

Show comment
Hide comment
@Art4

Art4 Mar 10, 2016

Contributor

@ethanresnick

If I understand you correctly, you're saying that, during development, minor versions act like major versions do during production (i.e., they may contain breaking changes).

Correct. See Semantic Versioning Specification point 4:

4.) Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.

The last years I have red a lot about API versioning and I'm a little disappointed that after more than one decade we don't have a standard or even a best practice for this. As a summery there are 3 major ways for versioning an API: URL, Custom Request Header and Content Negotiation which all have various disadvantages:

URL

Examples:

https://api.example.com/v1/resource
https://api-v1.example.com/resource
https://api.example.com/resource?version=1

Cons

Custom Request Header

Example:

GET /resource HTTP/1.1
Host: api.example.com
X-ApiVersion: 1.0
Vary: X-ApiVersion

Cons

Content Negotiation

Examples:

GET /resource HTTP/1.1
Host: api.example.com
Accept: application/vnd.myproduct.v1+json

or as an actual example in our case with JSON API:

GET /resource HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json; ext=com.example.api.version+0.15

Cons

So, no matter which way you go, you are doing it wrong. 😕

Contributor

Art4 commented Mar 10, 2016

@ethanresnick

If I understand you correctly, you're saying that, during development, minor versions act like major versions do during production (i.e., they may contain breaking changes).

Correct. See Semantic Versioning Specification point 4:

4.) Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.

The last years I have red a lot about API versioning and I'm a little disappointed that after more than one decade we don't have a standard or even a best practice for this. As a summery there are 3 major ways for versioning an API: URL, Custom Request Header and Content Negotiation which all have various disadvantages:

URL

Examples:

https://api.example.com/v1/resource
https://api-v1.example.com/resource
https://api.example.com/resource?version=1

Cons

Custom Request Header

Example:

GET /resource HTTP/1.1
Host: api.example.com
X-ApiVersion: 1.0
Vary: X-ApiVersion

Cons

Content Negotiation

Examples:

GET /resource HTTP/1.1
Host: api.example.com
Accept: application/vnd.myproduct.v1+json

or as an actual example in our case with JSON API:

GET /resource HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json; ext=com.example.api.version+0.15

Cons

So, no matter which way you go, you are doing it wrong. 😕

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Mar 10, 2016

Contributor

Not technically RESTful: http://www.troyhunt.com/2014/02/your-api-versioning-is-wrong-which-is.html

This is not true. REST says nothing about URLs, let alone the structure of URLs.

Versioning doesn't mean the resource changes, but the representation of the resource changes. So the URL should be the same, but you have to request another representation.

This is not always true. Version changes aren't always about only representation changes. If they were, then yes, the media type would be correct, as it would be about requesting a different representation.

Contributor

steveklabnik commented Mar 10, 2016

Not technically RESTful: http://www.troyhunt.com/2014/02/your-api-versioning-is-wrong-which-is.html

This is not true. REST says nothing about URLs, let alone the structure of URLs.

Versioning doesn't mean the resource changes, but the representation of the resource changes. So the URL should be the same, but you have to request another representation.

This is not always true. Version changes aren't always about only representation changes. If they were, then yes, the media type would be correct, as it would be about requesting a different representation.

@Art4

This comment has been minimized.

Show comment
Hide comment
@Art4

Art4 Mar 10, 2016

Contributor

@steveklabnik This is the old REST/not-REST fight. What I wanted to say is that this seems to be a choice between:

  1. violating the REST principles
  2. violating the ietf rfc or
  3. violating the IANA rfc?
Contributor

Art4 commented Mar 10, 2016

@steveklabnik This is the old REST/not-REST fight. What I wanted to say is that this seems to be a choice between:

  1. violating the REST principles
  2. violating the ietf rfc or
  3. violating the IANA rfc?
@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Mar 10, 2016

Member

@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 https://api.example.com/v1/x can be thought of "The x part of our v1 service" while https://api.example.com/v2/x can mean the distinct idea of "The x part of our v2 service".

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:

Meanwhile, if you keep the URI the same but only change the media type, making certain changes across major versions would be weird semantically, because media types are only supposed to control the format of the representation. For example, if you remove support for an HTTP method in v2 that was there in v1, you really want two distinct URIs, so you can have two distinct Allow headers and return 405 appropriately.

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

Member

ethanresnick commented Mar 10, 2016

@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 https://api.example.com/v1/x can be thought of "The x part of our v1 service" while https://api.example.com/v2/x can mean the distinct idea of "The x part of our v2 service".

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:

Meanwhile, if you keep the URI the same but only change the media type, making certain changes across major versions would be weird semantically, because media types are only supposed to control the format of the representation. For example, if you remove support for an HTTP method in v2 that was there in v1, you really want two distinct URIs, so you can have two distinct Allow headers and return 405 appropriately.

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

@bintoro

This comment has been minimized.

Show comment
Hide comment
@bintoro

bintoro Mar 10, 2016

Contributor

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.

Contributor

bintoro commented Mar 10, 2016

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.

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Mar 10, 2016

Member

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.

Member

ethanresnick commented Mar 10, 2016

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.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Mar 10, 2016

Contributor

Thank you @ethanresnick , you said it better than I.

Contributor

steveklabnik commented Mar 10, 2016

Thank you @ethanresnick , you said it better than I.

@bintoro

This comment has been minimized.

Show comment
Hide comment
@bintoro

bintoro Mar 10, 2016

Contributor

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 application/json spec doesn't prohibit applications from using proprietary parameters, yet the media type has been registered just fine.

Contributor

bintoro commented Mar 10, 2016

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 application/json spec doesn't prohibit applications from using proprietary parameters, yet the media type has been registered just fine.

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Mar 10, 2016

Member

@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.

Member

ethanresnick commented Mar 10, 2016

@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.

@ziege

This comment has been minimized.

Show comment
Hide comment
@ziege

ziege Mar 10, 2016

Contributor

@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...

Contributor

ziege commented Mar 10, 2016

@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...

@krainboltgreene

This comment has been minimized.

Show comment
Hide comment
@krainboltgreene

krainboltgreene Mar 10, 2016

Contributor

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 /v1/activities to /v2/activities I know that all my assumptions about the data and behavior have to change. When I see the Content-Type: application/lw.api+json; api-version=1 change to Content-Type: application/lw.api+json; api-version=2 I know my assumptions about the content and interface have to change.

Contributor

krainboltgreene commented Mar 10, 2016

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 /v1/activities to /v2/activities I know that all my assumptions about the data and behavior have to change. When I see the Content-Type: application/lw.api+json; api-version=1 change to Content-Type: application/lw.api+json; api-version=2 I know my assumptions about the content and interface have to change.

@masterspambot

This comment has been minimized.

Show comment
Hide comment
@masterspambot

masterspambot Mar 11, 2016

Contributor

Please take into consideration, that in most cases, company version their API facade through headers or urls like api/v1 mostly. So that to avoid collisions I'd still recommend what @Perni1984 said with accept header:

Accept: application/vnd.api+json; ext=version+1.1.0

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.

Contributor

masterspambot commented Mar 11, 2016

Please take into consideration, that in most cases, company version their API facade through headers or urls like api/v1 mostly. So that to avoid collisions I'd still recommend what @Perni1984 said with accept header:

Accept: application/vnd.api+json; ext=version+1.1.0

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.

@bf4

This comment has been minimized.

Show comment
Hide comment
@bf4

bf4 Apr 13, 2016

Contributor

Looks like this should be closed per #1020 ?

Contributor

bf4 commented Apr 13, 2016

Looks like this should be closed per #1020 ?

@pixelhandler

This comment has been minimized.

Show comment
Hide comment
@pixelhandler

pixelhandler Oct 12, 2016

Contributor

@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

application/vnd.api+json;version=1

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

Clients that include the JSON API media type in their Accept header MUST specify the media type there at least once without any media type parameters.

Contributor

pixelhandler commented Oct 12, 2016

@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

application/vnd.api+json;version=1

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

Clients that include the JSON API media type in their Accept header MUST specify the media type there at least once without any media type parameters.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Oct 12, 2016

Contributor

@pixelhandler if you read the first sentence of that post

Since I've posted this, I've refined a few of my positions on things. Everyone learns and grows, and while I still stand by most of what I said, 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.

Contributor

steveklabnik commented Oct 12, 2016

@pixelhandler if you read the first sentence of that post

Since I've posted this, I've refined a few of my positions on things. Everyone learns and grows, and while I still stand by most of what I said, 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.

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Oct 12, 2016

Member

I'm curious how the JSON API spec concluded without any media type parameters…

@pixelhandler Please read the thread, and comment back with any specific questions. The decision on media type parameters is well documented above, I think.

Member

ethanresnick commented Oct 12, 2016

I'm curious how the JSON API spec concluded without any media type parameters…

@pixelhandler Please read the thread, and comment back with any specific questions. The decision on media type parameters is well documented above, I think.

@pixelhandler

This comment has been minimized.

Show comment
Hide comment
@pixelhandler

pixelhandler Oct 12, 2016

Contributor

@steveklabnik ah I see thanks. @ethanresnick yeah reading it thanks.

I think that https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html allows for accept-extension and accept-params which afford custom params.

       Accept         = "Accept" ":"
                        #( media-range [ accept-params ] )
       media-range    = ( "*/*"
                        | ( type "/" "*" )
                        | ( type "/" subtype )
                        ) *( ";" parameter )
       accept-params  = ";" "q" "=" qvalue *( accept-extension )
       accept-extension = ";" token [ "=" ( token | quoted-string ) ]

Could accept-extension afford appending the Accept header with ;version=1 ?

Contributor

pixelhandler commented Oct 12, 2016

@steveklabnik ah I see thanks. @ethanresnick yeah reading it thanks.

I think that https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html allows for accept-extension and accept-params which afford custom params.

       Accept         = "Accept" ":"
                        #( media-range [ accept-params ] )
       media-range    = ( "*/*"
                        | ( type "/" "*" )
                        | ( type "/" subtype )
                        ) *( ";" parameter )
       accept-params  = ";" "q" "=" qvalue *( accept-extension )
       accept-extension = ";" token [ "=" ( token | quoted-string ) ]

Could accept-extension afford appending the Accept header with ;version=1 ?

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Oct 12, 2016

Member

@pixelhandler

Re accept-extension params, from upthread:

To @bintoro's point... there is a second type of parameter that the Accept header allows, called an extension parameter, which is distinct from media type parameters. So you could probably use that instead of using the URI or waiting on JSON API to add a suitable media type parameter. However, I've never actually seen anyone use an Accept extension parameter in real life, so I would expect interoperability issues, with some parsers mistakenly treating them as media type parameters or ignoring them entirely.

Member

ethanresnick commented Oct 12, 2016

@pixelhandler

Re accept-extension params, from upthread:

To @bintoro's point... there is a second type of parameter that the Accept header allows, called an extension parameter, which is distinct from media type parameters. So you could probably use that instead of using the URI or waiting on JSON API to add a suitable media type parameter. However, I've never actually seen anyone use an Accept extension parameter in real life, so I would expect interoperability issues, with some parsers mistakenly treating them as media type parameters or ignoring them entirely.

@bf4

This comment has been minimized.

Show comment
Hide comment
@bf4

bf4 Apr 18, 2017

Contributor

Was the conclusion here that Accept: application/vnd.api+json; version=3.0 is a legit way of saying my mime type is JSON:API and my api is at version 3? If so, how would JSON:API version 1.1 look?

Contributor

bf4 commented Apr 18, 2017

Was the conclusion here that Accept: application/vnd.api+json; version=3.0 is a legit way of saying my mime type is JSON:API and my api is at version 3? If so, how would JSON:API version 1.1 look?

@krainboltgreene

This comment has been minimized.

Show comment
Hide comment
@krainboltgreene

krainboltgreene Apr 18, 2017

Contributor

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.
B. It's not actually semantically correct (see @steveklabnik's words)
C. It's much easier to do path versioning (and have others understand): http://www.jbarnette.com/2009/04/07/http-apis.html

Contributor

krainboltgreene commented Apr 18, 2017

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.
B. It's not actually semantically correct (see @steveklabnik's words)
C. It's much easier to do path versioning (and have others understand): http://www.jbarnette.com/2009/04/07/http-apis.html

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Jul 15, 2017

Member

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?

Member

ethanresnick commented Jul 15, 2017

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?

@jamesplease

This comment has been minimized.

Show comment
Hide comment
@jamesplease

jamesplease Jul 15, 2017

Contributor

@ethanresnick , I’d be happy to write this up sometime over the next few days. How’s that sound to you?

Contributor

jamesplease commented Jul 15, 2017

@ethanresnick , I’d be happy to write this up sometime over the next few days. How’s that sound to you?

@ethanresnick

This comment has been minimized.

Show comment
Hide comment
@ethanresnick

ethanresnick Jul 16, 2017

Member

That would be awesome. Thanks James!

Member

ethanresnick commented Jul 16, 2017

That would be awesome. Thanks James!

@mblackritter

This comment has been minimized.

Show comment
Hide comment
@mblackritter

mblackritter Nov 3, 2017

Contributor

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

Accept: application/vnd.api+json;
Accept: application/vnd.api+json; version=1.0

By my experience that's accepted as by the specs by any implementation of JSON:API. 🤗

Contributor

mblackritter commented Nov 3, 2017

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

Accept: application/vnd.api+json;
Accept: application/vnd.api+json; version=1.0

By my experience that's accepted as by the specs by any implementation of JSON:API. 🤗

@mrhwick

This comment has been minimized.

Show comment
Hide comment
@mrhwick

mrhwick Nov 29, 2017

URI Versioning

Versioning 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, api/v1/user and api/v2/user are actually different resources, but I can choose to implement the api/v2/user resource as an identical representation to the api/v1/user resource, so you can use them interchangeably. You aren't guaranteed that they are identical, but I would probably document it as such and everything would be fine. In this scenario, you aren't guaranteed that a resource you create in v1 will appear in v2 since they are technically different resources, but I can (and probably would/should) implement it that way.

Header Versioning

Versioning 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 APIs

I 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.

mrhwick commented Nov 29, 2017

URI Versioning

Versioning 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, api/v1/user and api/v2/user are actually different resources, but I can choose to implement the api/v2/user resource as an identical representation to the api/v1/user resource, so you can use them interchangeably. You aren't guaranteed that they are identical, but I would probably document it as such and everything would be fine. In this scenario, you aren't guaranteed that a resource you create in v1 will appear in v2 since they are technically different resources, but I can (and probably would/should) implement it that way.

Header Versioning

Versioning 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 APIs

I 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment