Lessons learned designing APIs
Simple personal rules to avoid common mistakes and foster consistency while designing HTTP APIs.
Design the API first, write the service later.
Iterating over an API specification is easier and faster than in a real service. API specifications can autogenerate servers, clients, documentation, they will really save you more hours than you think. There are a bunch of API languages to help with this task 12
Even though I worked with RAML for several years, my personal favorite is OAS 3.0, it has teh good parts of RAML and OAS2, also several major companies backing it and creating tooling.
Hail the status codes.
As long as you use HTTP as underlying protocol, your API must follow the status codes. I've seen several times things like:
HTTP/1.1 200 OK
Content-Type: application/json
{
"ok": false,
"errors": ["Some error message"]
}
It is ok to have ok: boolean
as part of the response's body, the problem
comes when it is not consistent with the status code, and that may generate friction while
using different client tools or even confuse other services like load balancers.
The rule is easy even without using RESTful APIs
2xx
codes for successful results3xx
codes for redirections/responses without body-
4xx
codes for bad/invalid/unauthorized requests (user is doing something wrong) 5xx
codes for server errors- Complete list of status codes
At root level, always return objects, never arrays.
If you are returning a list, you can always return an object containing a list:
// do
type Response = {
data: Array<Element>
}
// instead of
type Response = Array<Element>
The chances you will have to add more metadata to your response are high, and compiled languages are not that happy parsing root level arrays.
Avoid "maps" when possible.
Compiled languages have a hard time optimizing generated code for maps or they don't even offer the feature without libraries. In almost any case the problem can be solved by returning an array of key-value pairs. It also adds the possibility to add more fields to the registers which is a good add-on for future features.
// do
type Response = {
data: Array<{
name: String
value: String
}>
}
// instead of
type Response = {
data: Map<String,String>
}
There is only one date format
ISO86019, and if possible, use it without timezones. It works everywhere, even in JavaScript.
2019-05-10T20:33:55Z
Without timezone (tailing Z)2019-05-10T23:33:55+03:00
With timezone (tailing offset)
You don't need GraphQL.
It often make simple things complicated. It adds a lot of blackboxes to clients and servers.
Every attempt to use GraphQL I've seen only added complexity to the projects and created a ton of unoptimized requests, and even denial of services.
Final note
I hope those recommendations (more will be added as they come) will avoid you some headaches. One last but not least important suggestion, adopt a consistent standard, it may be REST[^rest] (although nobody does RESTful APIs correctly) or JSON:API[^jsonapi] or something that doesn't exist yet, it only has to be consistent and it has to make sense.