Some REST API tricks

Photo by Omar Flores on Unsplash

Some REST API tricks

Simple tricks to make your REST API smarter

RESTful APIs are a well known concept in API design. They allow developers to leverage HTTP URLs and verbs to create versatile interfaces for CRUD operations as well as searching and filtering.

As a small recap, here are the HTTP verbs and what they are mapped to conceptually:

VerbURLBodyPurpose
GET/items-Retrieve all items
GET/items/123-Retrieve item 123
GET/items?owner=johndoe-Retrieve all John Doe items
POST/items{ "owner":"johndoe" }Add a new item
PUT/items/123{ "owner":"johndoe", "color": "red" }Replace the entire item 123
PATCH/items/123{ "color":"cyan"}Update only part of the item 123
DELETE/items/123-Delete item 123

So far so good. Now let's get things a bit more complicated: how can I achieve goals like mass update and mass delete?

VerbURLBodyPurpose
PATCH/items?color=green{ "color":"cyan"}Update green items to cyan
DELETE/items?color=black-Delete all black items
DELETE/items{ids: [1,2,3,...]}Delete all items with the specified ids

Note how the update action can only be implemented as a PATCH. If we need to replace all items matching a query parameter, we're in trouble and need to get fancy, stepping outside the pure REST world.

Another example of hitting the REST boundaries are the implementation of complex searches. Within a GET request, a query parameter or two is usually nothing more than a simple filter, but what if we need more convoluted search mechanism? We would ideally need something similar to this request:

VerbURLBodyPurpose
GET/items{"condition1":{"and": { "field1": { "equals": "value1"}, "field2":{"gte": 5}}}}Find all items matching condition1

There are two problems here:

  1. While allowed to any HTTP request, adding a body to a GET request is a bad idea.

  2. We hit the same endpoint with the same verb to perform a simple filter (or even a get all) and a complex query. While similar, those are different behavior, with the latter way more complicated than the former as the server has to parse and understand a custom query language.

So what is the solution? Firstly, we don't need to think RESTfully every time. Complex searches are outside the REST goals, thus we need to be fancier and create, for example, our own query language or convention. Secondly, we can create a REST-ish boundary between REST and non-REST world. That is:

VerbURLBodyPurpose
POST/items/search{"condition1":{"and": { "field1": { "equals": "value1"}, "field2":{"gte": 5}}}}Find all items matching condition1

This way we clearly separate the search endpoint from the REST ones and make our fancy mess. We can push this one step further by condensing common searches into aliases.

VerbURLBodyPurpose
POST/items/xmas-things-Find all x-mas items

Conclusion

This is a case where developers have to think a bit out of the box. What matters the most is a clean way of solving a problem, even if we need occasionally to step outside our proudly designed REST API.

To the next bite!

References