wheelmap-classic icon indicating copy to clipboard operation
wheelmap-classic copied to clipboard

API Versioning

Open schultyy opened this issue 8 years ago • 14 comments

This is a ticket which describes the general approach we want to follow when it comes to API versioning. Over the time we collected quite some features which will modify the current API in a way that it's backwards incompatible for current mobile clients. To prevent breakage of the current installed base and ensure that we're able to ship new features the next step in the long term is to introduce a new API version. The one currently in production will be treated as v1, the next one then as v2.

This ticket shall be the place where we can discuss possible implementation strategies and when this is going to happen.

schultyy avatar Nov 01 '16 15:11 schultyy

Plan: 1 PT for estimation by @schultyy

holgerd avatar Jan 03 '17 09:01 holgerd

I start to line up some ideas and thoughts regarding this topic. The current API cannot be touched in terms of URL structure. This would probably break behavior with all mobile clients deployed right now. So what we will do is to keep the old urls alive (/api/<path>).

All new features will be living in the /api/v2/ namespace. A thing which remains to be decided is if we port all existing functionality to v2 as well or if this will be left in /api.

schultyy avatar Jan 03 '17 13:01 schultyy

@schultyy It may be valuable to think about what happens for /v3/, /v4/ etc.

For example, say we have v4 of the API do we want the client to have to consider which API version they need to call for each call? Maybe it would be better to say "If there is no /v4/foo then redirect to /v3/foo and so on.

Hoverbear avatar Jan 03 '17 14:01 Hoverbear

@Hoverbear Concerning the redirect: You mean issuing a HTTP redirect to the client then?

schultyy avatar Jan 04 '17 09:01 schultyy

The original API namespace has to be preserved: https://github.com/sozialhelden/wheelmap/blob/master/config/routes.rb#L113-L168 to ensure that currently deployed clients still work as expected.

Routes

Inside of the api namespace we will start to define namespaces for v2, v3 etc which then contain the routes to the respective endpoints.

Versions

A new namespace must be defined if an API change breaks current behavior. A breaking change can be one of the following:

  • an endpoint is going to be removed
  • an endpoint is going to be renamed
  • the schema of response data is changed (renamed/removed keys)
  • The client now has to pass required parameters in order to get data where it got that data without these parameters before
  • Validation rules change so that data the client is sending right now is not valid anymore

Maintenance & Support

At some point we have to decide if it makes sense to drop support for legacy API versions. When do we drop support?

  • Immediately when a new API version comes out?
  • When a new version comes out + 1 year?

The big problem might be slow adoption of the new(er) API version(s) since users might not update Apps on their mobile devices frequently.

A sensible solution might be the following:

A new version comes out. From that point on new features are only introduced in the new API version. Older, now legacy API versions only receive (critical) bugfixes from now on.

schultyy avatar Jan 04 '17 13:01 schultyy

API Keys

Right now API Keys are passed via query parameter:

https://wheelmap.org/api/<path>?api_key=12345

For future API versions we might consider changing this because now they key will appear in web server logs and also will be cached since it's part of the URL.

schultyy avatar Jan 04 '17 13:01 schultyy

Follow up for API Keys:

Other APIs handle it like this for example:

  • Make use of the Authorization header: curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com

schultyy avatar Jan 09 '17 09:01 schultyy

@schultyy No I mean on the server it just gets transparently passed down. :) The client never notices or cares. Using a 301 redirect would incur a round trip.

Hoverbear avatar Jan 09 '17 09:01 Hoverbear

I've used the Authorization header before and it works quite well. Well supported too!

Hoverbear avatar Jan 09 '17 09:01 Hoverbear

Concerning API Keys:

During my work on #512 I discovered that the iOS client is already using the X-API-KEY header. So it needs to be decided if X-API-KEY shall be kept and made the required way for all clients or the token shall be passed via the Authorization header.

schultyy avatar Jan 12 '17 12:01 schultyy

Also topic we need to look into: How do we communicate API documentation in future? Use the current format or switch to something like Swagger?

schultyy avatar Jan 24 '17 09:01 schultyy

Another topic we ran into the other day during work for another feature:

Right now API responses are rendered via acts_as_api. I would like to keep that library in for the old v1 API but replace it with something else for newer API versions. The reason for that is that API responses shouldn't be coupled with models directly. We will run into scenarios where a response schema doesn't match the database schema and [acts_as_api] is not well equipped for that case. I'm not saying it can't do that but that case is not in the main focus.

A better fit would be the representable gem. It decouples models from their API representations. It also doesn't require an instance of a model to be passed in, so if required we could also use view models instead.

schultyy avatar Jan 31 '17 10:01 schultyy

Adding some notes and results from discussions which took place over the last few days:

We agree on doing API versioning via HTTP Accept-Headers in order to be able to keep URL schemes stable and versioning per resource. With this approach, we will do one thing differently though compared to current best practices:

When a client does not specify any Accept Header or an Accept-Header but without any version it will always get the latest representation/behavior of the resource. Since wheelmap and all API clients started without being aware of different API versions they do not specify any version in the Accept-Header. If we would stick to the latest version of our API this will result in failures eventually because the clients as they are deployed right now expect v1 of any resource. So to still be able to deliver v1 to them, as a default without any Accept-Header or a version specified we will always default to v1.

From a technical point of view, it seems reasonable to use the Versionist gem.

A todo which results out of the decision to use Accept-Headers is, to define a media type the clients then have to use eventually.

schultyy avatar Feb 10 '17 13:02 schultyy

This is on hold for now.

holgerd avatar Apr 05 '17 12:04 holgerd