REST and backward compatibility

Hi,

I'm struggling with the backward compatibility of a RESTful interface.
I can't find any documentation on the standard way to solving this. I
know that as long as you only extend the RESTful interface there is no
backward compatibility issue, but I don't believe that is a very
realistic situation (I refactor a lot). Say you want to change an
attribute's name, how would you solve this?

For the solution I've got the following assumptions:
- The interface version should not be placed in the URL (i.e.
www.mydomain.com/v2/books). The URL defines the resource, the
interface version is not part of the resource in my opinion.
- The interface version should not be placed in the parameters (i.e.
www.mydomain.com/books?version=2). This is an option, but it would
pollute my urls.
- Only the first interface version can be defaulted, because changing
the default will break existing client implementations using the
previous default. So this default is not really usefull in the long
run, since only version 1 can use it. But at least this would allow
people to leave out the interface version description, if they don't
have to be backward compatible.

I believe that an ideal solution to this would be to place the version
in the HTTP headers. Only there is no standard defined for it as far
as I can see. But it would be like the following:

POST www.mydomain.com/books HTTP/1.1
Interface-Version: 2
Content-Type: application/xml
Accept: application/xml

<book>....</book>

For the controller we could then apply a before_filter which rewrites
the param hash to the latest interface version (after determining the
used interface version from the header). The latest interface
definition is then used in the controller actions.

I think this can be quite clean.
Does anybody have know of a standard solution for this problem?

Regards, Bas

[edit_by_author]
The third assumption is incorrect. You must default the last version,
otherwise the non webservice clients (browsers etc.) would get into
trouble. But this does create another issue that every webservice
client must use the version number from the start, also for version 1.
[/edit_by_author]

I don’t think you should bother with version numbers, it’s RPC-like. Why do you want to keep an old version of the service alive? I would just force the clients to an update, or provide a new service for the updated resources.

But if you want to use a version number, both examples are equal. All the information that the server needs to fulfill the request should be in the path. It doesn’t matter much if it’s /v2/books or books?v=2.

I would just fork the project and have it running on diffrent domains, or rewrite the URL on the incoming webserver to point to different applications.

Maintaining different versions off an API is going to be hell.

Hi Matthijs,

I'm afraid I cannot just force my paying customers to change their
interfaces. I don't think you ever can... in real life (only the
planning and synchronized execution alone would give severe
headaches).
Anyway I did some more reading and I think the HTTP Header option is
not so bad. There are X-Headers which basically are free form. So the
header would look like the following:

POST /books HTTP/1.1
User-Agent: ApacheBench/2.0.40-dev
Authorization: Basic YmFzOnRlc3Q=
Host: www.mydomain.com
X-Interface-Version: 2
Content-Type: application/xml
Accept: application/xml

The before_filter is really not so hard to implement. And making the
interface upgrades cascaded (in case of a version delta > 1) will only
require me to add a single mapping for each new interface version,
only defining the mapping with the previous version. Keeping in mind
that interface versions are only increased is case of a non-
incremental extension, I won't expect to ever go beyond more than 3
interface versions in practice.

So I think your statement that "Maintaining different versions off an
API is going to be hell." is not necessarily the case. As I see things
now, the backward compatibility issue with RESTful interfaces is
solvable nicely in Ruby on Rails (or in general).

The only remaining question is: Am I doing something really non-
standard and is there a better solution?

Regards,
Bas van Westing