API Design Goals
An API is a documented way in which external consumers interact with code. It is the contract: what the code does, not how.
WhatsApp exposes getAdmins to find all the admins of a group. It takes a group ID as a string and returns a list of admin objects named admins. It has two errors: the group does not exist, or the group is deleted.
The design runs through a checklist:
- Where it is exposed. In a microservice architecture, getAdmins belongs to the group service.
- Naming. The action defines the name. An API that also returns the groups the admins belong to is no longer getAdmins.
- Parameters. No additional parameters, except to optimize: information that saves the service four or five IO calls to other microservices.
- Response. A response stuffed for extensibility gives the caller more than it needs, costs network, and is confusing.
- Errors. Defining every possible error and returning one generic error are both extremes. A group that does not exist is an expected error, and a generic 404 for it is wrong.
API Design in Practice
An API on an HTTP endpoint has a route that says where the function is, an action, and request and response objects.
The route holds the address of the site, the model, the function and a version such as v1. A POST to getAdmins carries the group ID as a JSON object, and the response is a JSON array of admins.
Four rules of a clean API:
- Routing is kept apart from the action. A request with only a group ID is ambiguous.
- HTTP already defines GET, so the function is named admins.
- No side effects. A setAdmins that also adds members or creates the group does more than its name says. A missing group returns 404, and the client calls create group.
- A huge response is broken into pieces. Pagination gives the client an offset: of 200 members, the first 10 and then the next 10. Fragmentation numbers the pieces between services.
Perfect consistency is slow. A cached response is slightly older, and returning only the essentials under database load is service degradation.