Mon 09 December 2013

Here I want to consider implementation of API best practices which
usually don't follow Fielding's REST strictly. Example Flask project
is on GitHub.

API Versioning

Interfaces are changed hence versioning is mandatory in order to not annoy
your users. You might need to add new resource or field to particular resource.
You write code, tests and update documentation. Users are happy.
It's possible that you have to rename or delete field of some resource.
This case is harder and you might make the easiest decision — spawn
a lot of if statements and write more tests consequently.
Code base maintaining is getting worse.

I think it's better to make API versions isolated.
It will keep things simple and tests as well. You may even want to change
framework if legacy API implementation is not good enough.
For example, you have two WSGI applications. Each application implements
certain API version, e.g, messaging_api_v1, messaging_api_v2.
In order to hide versioning information from applications, e.g., URL prefix,
you can dispatch requests by Werkzeug's DispatcherMiddleware.

URLs

API resources are nouns, so /messages/ URL is for a collection of messages.
You should think about it as UNIX folder. Hence /messages is correct
folder path and Flask will redirect to the canonical URL /messages/.
Certain message's /messages/1 URL must not contain trailing slash
in order to look like UNIX file path.

Resources usually have relationships and they might be expressed in URLs,
e.g., get messages from account which id is 1.

$ curl https://api.example.com/v1/accounts/1/messages/

Representation

There are two popular approaches to specify response format. First one
is based on Accept header, second — URL based. Here I use header based
approach.

I wrote ResponsiveFlask class which extends Flask by supporting
dictionary response. Views can return dict and it will be represented
based on Accept header. When ResponsiveFlask.make_response() receives
dictionary it creates real response object using appropriate formatter.
Formatter is picked by mimetype.