114

What is the best way to version REST URIs? Currently we have a version # in the URI itself, ie.

http://example.com/users/v4/1234/

for version 4 of this representation.

Does the version belong in the queryString? ie.

http://example.com/users/1234?version=4

Or is versioning best accomplished another way?

Charles
  • 50,943
  • 13
  • 104
  • 142
Mike Pone
  • 18,705
  • 13
  • 53
  • 68
  • 1
    Possible duplicate of [Best practices for API versioning?](https://stackoverflow.com/questions/389169/best-practices-for-api-versioning) – Helen Oct 10 '17 at 22:43

11 Answers11

193

Do not version URLs, because ...

  • you break permalinks
  • The url changes will spread like a disease through your interface. What do you do with representations that have not changed but point to the representation that has? If you change the url, you break old clients. If you leave the url, your new clients may not work.
  • Versioning media types is a much more flexible solution.

Assuming that your resource is returning some variant of application/vnd.yourcompany.user+xml all you need to do is create support for a new application/vnd.yourcompany.userV2+xml media type and through the magic of content negotiation your v1 and v2 clients can co-exist peacefully.

In a RESTful interface, the closest thing you have to a contract is the definition of the media-types that are exchanged between the client and the server.

The URLs that the client uses to interact with the server should be provided by the server embedded in previously retrieved representations. The only URL that needs to be known by the client is the root URL of the interface. Adding version numbers to urls only has value if you construct urls on the client, which you are not suppose to do with a RESTful interface.

If you need to make a change to your media-types that will break your existing clients then create a new one and leave your urls alone!

And for those readers currently saying that this makes no sense if I am using application/xml and application/json as media-types. How are we supposed to version those? You're not. Those media-types are pretty much useless to a RESTful interface unless you parse them using code-download, at which point versioning is a moot point.

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • 70
    To address the bullet points. 1. you don't break perma links, because permalinks link to a specific version 2. If everything is versioned that this isn't an issue. Old urls can still work. Ideally, you wouldn't want a version 4 URL returning an association to a version 3 resource. 3. Perhaps – Mike Pone Jun 11 '09 at 20:45
  • 10
    Imagine if when you upgraded to a new version of a web browser, all your bookmarked favourites broke! Remember that conceptually the user is saving a link to a resource, not to a version of a representation of a resource. – Darrel Miller Jun 12 '09 at 00:39
  • 1
    @Darrel, I agree with everything you wrote except for the comment about xml/json media-types being useless for RESTful interfaces. Can you please elaborate? I don't understand what you mean... – Gili Feb 23 '10 at 04:35
  • 11
    @Gili In order to satisfy the requirement for a REST api to be self-descriptive it is necessary that the content-type header provide the complete semantic description of the message. In other words, your media type is your data contract. If you deliver application/xml or application/json you are telling the client nothing about what is contained in that XML/Json. The instant that a client application reaches in a pulls out /Customer/Name you are creating coupling that is based on information that is not in the message. Eliminating out-of-band coupling is critical to achieving RESTfulness. – Darrel Miller Feb 23 '10 at 13:34
  • 2
    @Darrel, doesn't the combination of URL and XML/JSON tell you the exact format to expect? If not, what do you recommend instead? – Gili Feb 23 '10 at 13:56
  • 6
    @Gili The client should have no prior knowledge of the URLs of the API other than the root URL. You should not tie representation formats to specific URLs. When it comes to choosing media-types you really need to chose between a specific format like application/vnd.mycompany.myformat+xml or a standardized one like, XHtml, Atom, RDF, etc. – Darrel Miller Feb 23 '10 at 15:49
  • @Darrel, the specification states that page A links to page B but does not give out the URL of the latter. It goes on to state the exact format that the JSON representation for page B will take. Isn't this completely RESTful? – Gili Feb 23 '10 at 16:12
  • 2
    @Gili The problem is that each request should be independently self-descriptive. If you copy the URL for page B and email to a friend then you have lost the context. The other problem is that in order to define the spec for page A you would need to create a custom media type anyway. – Darrel Miller Feb 23 '10 at 23:01
  • 1
    Here is a good article that explains how to version the resource representation: http://www.informit.com/articles/article.aspx?p=1566460 – Gili Oct 01 '10 at 04:26
  • @Darrel, whether you version the URI or content-type doesn't the new version still "spread like a disease"? For example, if URI "a" Version 2 points to URI "b" then the latter must also provide Version 2 or else clients must support both Version 1 and Version 2 formats simulatenously. What do you suggest? – Gili Oct 01 '10 at 04:35
  • 2
    @Gili You are assuming that the service only returns a single media type. Imagine a service that supports `application/vnd.acme.document` and 'application/vnd.acme.validationrules'. Each "document" points to a another resource that contains "validationrules". By versioning media types I can handle breaking changes to validationrules by introducing `application/vnd.acme.validationrulesV2` without creating a new version of 'application/vnd.acme.document' – Darrel Miller Oct 01 '10 at 11:23
  • @Gili Here's another article on the subject http://barelyenough.org/blog/2008/05/versioning-rest-web-services/ – Darrel Miller Oct 01 '10 at 11:27
  • @Darrel, the link you mentioned is great but it still doesn't answer the question of what happens if resource A has versions 1 and 2 and it points to resource B that only has version 1. Is a client really supposed to remember which version it should request for which resource? "Anytime someone references A, regardless of their version, make sure to ask for A version 2". Is that correct? – Gili Oct 01 '10 at 17:50
  • @Gili That's actually what the accept header does. It declares to the server all the media types that the client can support. – Darrel Miller Oct 01 '10 at 18:17
  • @Darrel, Say you write a client that supports version 2 of the protocol (a compile-time decision), how is using the accept header (a run-time decision) going to help? Don't I have to decide at compile-time which version of each page I will request? I don't think it's realistic for version 10 clients to also support versions 1 through 9 and "do their best" depending on what the Accept header returns at run-time. – Gili Oct 03 '10 at 04:09
  • 1
    @Gili The client sends the Accept header to the server to declare what versions it supports. It is the server that needs to support all versions, not the client. Effectively the Accept header is baked into the client at compile time. – Darrel Miller Oct 03 '10 at 11:04
  • @Darrel, so if I understand you correctly, you agree that a client must be baked at compile-time with a [URI, Version] mapping so that at run-time it knows that even though Resource A is version 2, when it references Resource B the latter should be retrieved using version 1. When it hits resource A (regardless of where it got the URI) it uses "Accept: application/vnd.ResourceAv2". When it hits resource B (regardless of where it got the URI) it uses "Accept: application/vnd.ResourceBv1". Is that correct? – Gili Oct 04 '10 at 00:33
  • 2
    @Gili Not sure I follow. No URI's are baked in. The client also does not know what media type is going to come back from any URI. The client should decide what to do based on what comes back. All it knows is that it knows how to handle application/vnd.ResourceAv2 and application/vnd.ResourceBv1 so it sends Accept: application/vnd.ResourceAv2, application/vnd.ResourceBv1 to every URI that it requests. – Darrel Miller Oct 04 '10 at 02:22
  • 1
    @Darrel: Okay, that last sentence confirms what I was asking: the client sends Accept: to every URI. Thanks. – Gili Oct 05 '10 at 00:13
  • 4
    Does it make sense to put the API version is a separate header field? Like so: Accept: application/com.example.myapp+json; version=1.0 – Erik Apr 29 '12 at 16:23
  • 2
    I didn't know that you could define your own media types; I assumed there were standards (like application/xml and application/json). And frameworks like Jersey precipitate this fallacy thanks to things like media type enums. – smcg Oct 23 '12 at 20:39
  • the reason to add version to the URL is the change of representation of resource, so a new media type is preferable. However, in reality, it seems that many companies put the version info into the URL. it may beacuse that reason proposed by dcerecedo in [an answer](http://stackoverflow.com/a/6750376/1889327) to the similar question. It may be that version in URL is easily to debug and more readable to people. – andy Nov 25 '13 at 01:36
  • 1
    So Google made the wrong decision when creating all of their REST API's ? :/ As mentioned [here](http://www.informit.com/articles/article.aspx?p=1566460) and probably on numerous other places, there isn't necessarily a *good* and a *bad* way to do versioning. Each solution has its advantages and disadvantages, and depending on the target API consumers and use case, one may be more appropriate than the other. – Vincent Sels Oct 23 '15 at 07:10
  • 1
    @VincentSels If I had a dollar for every times made a wrong decision I wouldn't have to work. Also, I should probably update this answer as my position has evolved since I wrote this 6 years ago. – Darrel Miller Oct 23 '15 at 18:47
  • I think [Troy Hunt's blog post](https://www.troyhunt.com/your-api-versioning-is-wrong-which-is/) i good concerning the three evils (url vs header param vs Accept Header). This article is explaining why the author thinks "Accept Headers" is best. – Avec Nov 29 '16 at 19:51
35

I would say making it part of the URI itself (option 1) is best because v4 identifies a different resource than v3. Query parameters like in your second option can be best used to pass-in additional (query) info related to the request, rather than the resource.

Zef Hemel
  • 6,457
  • 3
  • 20
  • 14
  • 12
    The question is, is it a different RESOURCE we are discussing? Or a different representation of that resource? Does REST make a distinction between the representation and the resource? – Cheeso Jun 09 '09 at 20:16
  • 1
    @Cheeso - The OP indicates that it is a different representation rather than a different resource, hence my answer. – Greg Beech Jun 09 '09 at 20:24
  • This has been answered in a greater detail before here http://stackoverflow.com/q/389169/104261 – Taras Alenin May 04 '12 at 00:36
  • +1 for "Query parameters like in your second option can be best used to pass-in additional(query) info related to the request, rather than the resource" – andy Mar 25 '14 at 04:08
  • For different representations I think you should use headers like "Accept", then the client can specify to the server "I accept only version 4" and the server can answer with that representation. If no accept is sent then the last version is provided. – Carlos Verdes Jan 22 '17 at 06:21
22

Ah, I'm putting my old grumpy hat on again.

From a ReST perspective, it doesn't matter at all. Not a sausage.

The client receives a URI it wants to follow, and treats it as an opaque string. Put whatever you want in it, the client has no knowledge of such a thing as a version identifier on it.

What the client knows is that it can process the media type, and I'll advise to follow Darrel's advice. Also I personally feel that needing to change the format used in a restful architecture 4 times should bring huge massive warning signs that you're doing something seriously wrong, and completely bypassing the need to design your media type for change resiliance.

But either way, the client can only process a document with a format it can understand, and follow links in it. It should know about the link relationships (the transitions). So what's in the URI is completely irrelevant.

I personally would vote for http://localhost/3f3405d5-5984-4683-bf26-aca186d21c04

A perfectly valid identifier that will prevent any further client developer or person touching the system to question if one should put v4 at the beginning or at the end of a URI (and I suggest that, from the server perspective, you shouldn't have 4 versions, but 4 media types).

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
SerialSeb
  • 6,701
  • 24
  • 28
  • What if the representation needs to change significantly and won't be backwards compatible? – Mike Pone Jun 11 '09 at 14:48
  • 1
    By designing your media type in an extensible fashion, such as by using namespaces and an extensible xsd, or existing xml formats ike atom, this should be preventable. If you really have to, another media type is the way to go. – SerialSeb Jun 16 '09 at 10:19
  • 1
    I like this completely valid answer, but I think the proposed URI is more to demonstrate the point than for a real scenario in which you do want 'hackable' URIs. – Dave Van den Eynde Oct 31 '12 at 10:40
7

You should NOT put the version in the URL, you should put the version in the Accept Header of the request - see my post on this thread:

Best practices for API versioning?

If you start sticking versions in the URL you end up with silly URLs like this: http://company.com/api/v3.0/customer/123/v2.0/orders/4321/

And there are a bunch of other problems that creep in as well - see my blog: http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

Community
  • 1
  • 1
jeremyh
  • 5,233
  • 3
  • 23
  • 19
  • 14
    Sorry, but I don't think you do end up with silly URLs like this. You are tying version numbers to a particular resource or (worse) to a particular representation. That would be silly, IMO. Rather, you are versioning the API, so you'd never have more than one version in the URI. – fool4jesus Jan 08 '13 at 10:07
5

There are 4 different approaches to versioning the API:

  • Adding version to the URI path:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    

    When you have breaking change, you must increment the version like: v1, v2, v3...

    You can implement a controller in you code like this:

    @RestController
    public class FooVersioningController {
    
    @GetMapping("v1/foo")
    public FooV1 fooV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping("v2/foo")
    public FooV2 fooV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Request parameter versioning:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    The version parameter can be optional or required depending on how you want the API to be used.

    The implementation can be similar to this:

    @GetMapping(value = "/foo/param", params = "version=1")
    public FooV1 paramV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/param", params = "version=2")
    public FooV2 paramV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Passing a custom header:

    http://localhost:8080/foo/produces
    

    With header:

    headers[Accept=application/vnd.company.app-v1+json]
    

    or:

    headers[Accept=application/vnd.company.app-v2+json]
    

    Largest advantage of this scheme is mostly semantics: You aren’t cluttering the URI with anything to do with the versioning.

    Possible implementation:

    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
    public FooV1 producesV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
    public FooV2 producesV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • Changing Hostnames or using API Gateways:

    Essentially, you’re moving the API from one hostname to another. You might even just call this building a new API to the same resources.

    Also,you can do this using API Gateways.

Javier C.
  • 7,859
  • 5
  • 41
  • 53
5

These (less-specific) SO questions about REST API versioning may be helpful:

Community
  • 1
  • 1
Pete TerMaat
  • 3,185
  • 25
  • 15
3

I wanted to create versioned APIs and I found this article very useful:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

There is a small section on "I want my API to be versioned". I found it simple and easy to understand. The crux is to use Accept field in the header to pass version information.

Asma Zubair
  • 418
  • 1
  • 4
  • 11
2

If the REST services require authentication before use, you could easily associate the API key/token with an API version and do the routing internally. To use a new version of the API, a new API key could be required, linked to that version.

Unfortunately, this solution only works for auth-based APIs. However, it does keep versions out of the URIs.

UberSteve
  • 161
  • 3
2

If you use URIs for versioning, then the version number should be in the URI of the API root, so every resource identifier can include it.

Technically a REST API does not break by URL changes (the result of the uniform interface constraint). It breaks only when the related semantics (for example an API specific RDF vocab) changes in a non backward compatible way (rare). Currently a lot of ppl do not use links for navigation (HATEOAS constraint) and vocabs to annotate their REST responses (self-descriptive message constraint) that's why their clients break.

Custom MIME types and MIME type versioning does not help, because putting the related metadata and the structure of the representation into a short string does not work. Ofc. the metadata and the structure will frequently change, and so the version number too...

So to answer your question the best way to annotate your requests and responses with vocabs (Hydra, linked data) and forget versioning or use it only by non backward compatible vocab changes (for example if you want to replace a vocab with another one).

inf3rno
  • 24,976
  • 11
  • 115
  • 197
1

I'd include the version as an optional value at the end of the URI. This could be a suffix like /V4 or a query parameter like you've described. You might even redirect the /V4 to the query parameter so you support both variations.

Paul Morgan
  • 31,226
  • 3
  • 24
  • 27
0

I vote up for doing this in mime type but not in URL. But the reason is not the same as other guys.

I think the URL should be unique (excepting those redirects) for locating the unique resource. So, if you accept /v2.0 in URLs, why it is not /ver2.0 or /v2/ or /v2.0.0? Or even -alpha and -beta? (then it totally becomes the concept of semver)

So, the version in mime type is more acceptable than the URL.

Yarco
  • 763
  • 9
  • 18