Talk:API versioning

Options for entry point versioning
While we generally try to evolve the responses returned by entry points in a backwards-compatible manner, there is occasionally a need to change the format returned by an entry point in a way that breaks backwards compatibility. To avoid breaking clients that might not be able to change their code in a timely manner (think apps that users might not update for a while), we should provide means for a client to communicate the client's expectations as part of the request.

The main methods for per-entry point versioning are:


 * Adding another a path element in the URL, as in.
 * Using a query string, as in.
 * Using the  header, indicating the expected mime type.
 * Using other custom headers.

Here is a discussion of the relative pros / cons:

Query strings
Append a  or   query parameter to the URL to request a specific format version. Return the latest version if no query parameter is supplied.

Caching
Vary is naturally enforced by having different URLs. Purging is complicated by the need to purge all variants separately. There are features like Varnish 4's X-Key index that can enable this. Alternatively, a VCL hack could move ?accept=... to a header & then vary on that.

Ergonomics & documentation
Swagger supports query parameters well, and even lets the user select format versions from a drop-down. Communicating a URL is simple, and it is relatively simple to remove the query string to link to the latest format version where desired.

Analytics
For the desirable grouping of request metrics independent of version negotiation, the query string needs to be stripped from the URL. This is not very complicated to do, but might not be done by default.

Semantics / standards
The use of query strings is not standardized, and no widely-used format for version parameters exists.

header
Return the latest format if no Accept header is supplied, or no specific content-type matches and  is accepted. Return the requested format if it is (still) supported by the API.

Caching
Browsers send  headers by default, indicating their preference for headers and images. This means that a naive  would fragment caches for requests with the default header set. However, our content types all contain a version parameter using the  mechanism. We can use this to derive a secondary cache key using the HTTP "Key" header, once it is implemented in Varnish. Until this is implemented, we can use a bit of VCL very similar to what we already do for cookie headers to distinguish default  header values from explicit requests for a specific mime type.

Purging is fairly simple in this scheme, as all format variants of a logical resource share the same URL.

Ergonomics & documentation
Generally, sending headers is slightly more complex than just copy & pasting a URL. However, CORS allows setting  headers by default. The swagger documentation already emits an example curl invocation with the right Accept header.

If we always return the latest version if the Accept header is omitted, example URLs will normally still point to the same resource. In documentation scenarios, linking to the latest version is typically desirable.

Analytics
Having a single URL per resource makes it easy to provide aggregate access statistics independent of content version negotiation. Statistics about the relative popularity of individual versions can be collected separately (and are normally per-entry point rather than resource), and can be used to determine when to drop support for an old mime type.

Semantics / standards
The use of  for content negotiation has been part of HTTP from the beginning, and is widely supported. Its semantics provide a nice symmetry between request and response content-type, and generally emphasize the role of the content-type in describing the resource.

Related reading

 * 3 different wrong ways of versioning API end points