Djangocon: creating solid APIs - Rivo Laks

Tags: djangocon, django

(One of my summaries of a talk at the 2018 european djangocon.)

Our applications are increasingly also used by other computers. But the APIs they use are ironically aimed at humans: at the programmers that have to program on your API.

“API” means “application programming interface”, but it might be better to say “application programmer interface” as you really have to focus on the programmer that has to do the programming!

What makes an API good?

  • Documentation.

  • Familiarity.

  • Lack of friction.

Documentation

This is often overlooked. But it is the first point of contact of your API, so it gives the first impression. An important deciding factor. It takes lots of work to write good documentation, but the effort is worth it.

Look at your documentation as a “sales page” for your API. What do you want on your sales page? Here are some suggestions:

  • How do I access it?

  • Do I need authentication? How?

  • What is the root URL?

  • General info on encodings and formats. Make it explicit.

  • Pagination.

  • Document the common errors (error codes) you can get.

  • Code for getting started. It gives your users a warm feeling if their first try works right away.

When you document the endpoints, show the URL and the available operation. Also show the request and response data formats. Don’t forget the optional parameters. When permissions change what you can do with an endpoint: mention it.

How do you keep your documentation up-to-date? The solution is to automatically generate the documentation. The common solution is to first generate a machine-readable schema out of your code and then generate documentation out of the schema.

OpenAPI and swagger are two tools you could use. But he advises to look at your own (django) tools first: what is available in there?

Once you have a schema, you could look at generating client libraries…

Familiarity/standardization

Important: standardization! People are used to exiting APIs. Familiarity helps.

You could look at http://jsonapi.org, “a specification for building APIs in json”. GraphQL is another option. (So: jsonapi.org is just an example.)

A standard structure (as suggested by jsonapi.org) helps, like a links item in the result with prev/next URLs for the next/previous paginated pages. Then a data item with type, ID, self-url, attributes, further links etc.

If you requested a single item (/projects/12) you get a single item in data, if you requested a collection (/projects), data should be a list.

The big advantage is that every API that uses this standard feels familiar. It is easier to work with.

jsonapi.org has support for limiting the number of returned fields (?fields=name,id) and for including nested data ?include=comments. Essential if you want to tweak the output for better performance.

Lack of friction

Errors will happen. How easy it is to react to errors is quite important in reducing friction. jsonapi.org lets you return nice usable errors.

Friction can also happen when you work with large datasets. jsonapi.org suggests to add out-of-band URLs (/download/all-the-data.tgz).

Friction can also come from differences between versions of your API. The best you can do is to build in versioning right from the start. There are a couple of approaches:

  • Use headers. AcceptHeaderVersioning like django rest framework uses.

    Theoretically, this is the neatest option. But there are practical problems.

  • Embedding the version in the path (/api/v1/projects) is the most common solution. Django rest framework supports it.

Versions need a scheme. Do you use 1.0, 2.0, v1, v2? Or date based numbers? The latter are less emotional.

You could look at “version transformers”. Your core code would only support the latest version. A transformer should transform any old requests to the latest version (and the other way around for transforming responses). This approach makes it easier to make small changes like changing field names.

It obviously won’t work for massive changes. For that, you probably have to create an entire new API and duplicate some code.

Authentication/authorization

For authentication and authorization, there are basically two good solutions:

  • Token authentication. Clients send a HTTP header. Session cookies can also be seen as a form of token authentication (for when your API is only accessed from browsers).

  • OAuth 2.0. Good for creating platforms. It is complex, but it solves many issues.

    Luckily there are many libraries that help you with the low-level plumbing. OAuthlib, django oauth toolkit, etc.

tg-apicore

Something to look at: tg-apicore, an add-on for django rest framework that enables jsonapi.org. It also helps with documentation generation.

There are similar projects that do the same for other standards.

https://abload.de/img/screenshot2018-03-07a4qrub.png

Photo explanation: painting crates, building a shelf out of thick strips of paper, etc.

 
vanrees.org logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):