REST API design patterns

Some feedback for API design

Published on November 3, 2018

How many different endpoints do you need to manage your resources? Who decides on the resource IDs? How to update the resources? During my adventure in building Waziup REST API, I found some questions difficult to answer. They were questions about the overall design, rather than design of specific endpoints. Here is some feedback on those questions.

GET-POST-GET-DELETE

For managing resources, the basic pattern is GET-POST-GET-DELETE. For instance:

The first GET allows to retrieve a bunch of resources. It usually comes with some query parameters allowing to filter and sort the resulting list: for instance limit, offset, dateFrom and dateTo. POST will create new resources. The second GET will retrieve a single resource. Finally, the DELETE will remove the resource.

Server-side or Client-side IDs?

An important design question is to decide if the server will select the ID of the resource, or the client. If the server do, the resource ID is generally returned in the POST response body:

$ curl -X POST http://localhost:8080/houses -d '{"owner": "cdupont"}
087bccd2-ef47-420b-909a-0ef22901b570

The resource unique ID is returned: 087bccd2-ef47-420b-909a-0ef22901b570. In this example the server generates a UUID for our resource. It will be used to further manipulate the resource:

$ curl -X GET http://localhost:8080/houses/087bccd2-ef47-420b-909a-0ef22901b570
{"owner" : "cdupont"}

However, in some cases the client can decide the resource ID. In this case, the ID will be included in the request body:

$ curl -X POST http://localhost:8080/houses -d '{"id": "MyHouse", "owner": "cdupont"}

In this case, the server must check the unicity of the resource. If a resource with the same ID already exists, an error will be returned: 422 Already exists.

So which solution should you choose? In my opinion, it depends on where most of the business logic resides. In the majority of the cases, the resource is managed server-side: the server knows what the resource represents, and performs a lot of transformations on it. In this case, a server-side ID is clearly indicated. In other cases, the server is just a database: the business logic belongs to the client. In this case, the client can decide on the ID. This is the case with Orion entities.

Updating resources: PATCH or PUT?

Another question that arises is how to update the resources. There are two choices: PUT or PATCH. PUT is more verbose, because each field in your data structure will need one endpoint. For example, here is how to update the owner of a house:

$ curl -X PUT http://localhost:8080/houses/087bccd2-ef47-420b-909a-0ef22901b570/owner -d "cdupont"

PATCH is more concise: only one endpoint can handle all updates.

$ curl -X PATCH http://localhost:8080/houses/087bccd2-ef47-420b-909a-0ef22901b570 -d '{"owner": "cdupont"}

I personaly prefer the PUT pattern, because it displays more clearly what you can, and cannot modify in the API.

Headers

Some headers are often misunderstood: Content-type and Accept. However they are rather simple:

Thus, when passing data to the server, you need to indicate what format your data is in:

$ curl -X POST http://localhost:8080/houses -H 'Content-type: application/json' -d '{"id": "MyHouse", "owner": "cdupont"}

If you expect some data to be returned by the server, you need to mention in what format you want that data:

$ curl -X GET http://localhost:8080/houses/087bccd2-ef47-420b-909a-0ef22901b570 -H 'Accept: application/json'
{"owner" : "cdupont"}

Most common data types (mime-types) are: application/json for JSON, and text/plain for a simple string.

Comments