I'm going to show you my conclusions first, and then we'll work backwards and talk about why each of these things are important. If you just read this list and go forth to build an API that checks all these boxes, I'm proud of you. If you're skeptical about why all these things—all these things—are essential, keep reading and we're going to get into it.
This is a process I’ve used countless times, for tiny projects and for huge projects. It leaves very little room for bikeshedding. By framing it like this, non-technical stakeholders can help, and often they have important insights that the engineers might not be thinking about.
- Serve JSON over HTTP.
- Every single possible valid request is defined by an OpenAPI spec that is freely available to every user of your API.
- Every request to your API can be described as retrieving, updating, or deleting a resource.
- You use GET for retrieving, POST for creating, PATCH for updating, and DELETE for deleting.
- Every path fits one of the following patterns:
/resource_type
represents a collection of resources./resource_type/resource_id
represents a single resource, identified by ID./resource_type/resource_id/relationship_name
represents the resource or resources that are related to the identified resource with the given relationship name.
- Your responses are always objects. Successful responses have a
data
key that is an object or an array of objects; erroneous responses have anerror
key that is an array of strings. - A request to retrieve a collection can be paginated, ordered, and filtered using query parameters.
- If your API supports authenticated access, requests are authenticated with an
Authorization
header. - Resource types (both in your paths and in your JSON objects) are plural names (like
"posts"
). Relationships are singular or plural based on if they are to-one or to-many, respectively.
We're going to get into all the details, but just keep all of this in mind.