Background
I am coding a custom API backend, and as part of that end up needing to design that API. Inevitably I aim for REST and thus meet its challenges.
The problem
The design choice I am looking into is how to handle operations over multiple objects. The main case I consider is deletions of sessions, since a hypothetical shared/“shared”(aka. hacked) account will have enough of them that deleting them individually will have significant overhead and delay could be a real issue.
There is the
The solutions
Since REST as a concept is pretty much as old as HTTP there are some traditional solutions to this. The ones I have found (and their properties) are:
Don’t solve it
Users probably won’t use this multiple operation feature and may be confused by it. Just don’t bother and perhaps instead work on minimizing the overhead on small operations.
Advantages:
- it requires no additional effort
- keeps the API simple and predictable for users
- granular error reporting
Disadvantages:
- performance “loss” (absence of gain) on large user operations
- potential user frustration if they would benefit from operation grouping
Summary:
Probably the best option for an API where users don’t have that many objects, since there isn’t much performance to gain from grouping unless a lot of objects need changing.
If there is a specific case where you need to support, consider if you could solve it specifically instead of generally (in my case, support deleting all sessions for a user on a separate path).
Allow operations on collections
Since REST APIs are already recommended to handle filtered GET requests on collection paths it would be a reasonable expansion to allow filtered DELETE requests on the same. Since this can be translated into a single database query using those filters (at least in my case) it is no more prone to errors, but this may vary depending on use case.
Advantages:
- code symmetry (GET / DELETE)
- predictable
- good performance (the best if you collapse it into a single database query)
Disadvantages:
- the same cannot easily be done for other VERBS
- introduces gotcha (DELETE on collection with no filter)
- less granular error reporting (but unlikely to be needed due to ->)
- very limited operation grouping (only within a collection path)
Summary:
A reasonable middle way for when users are likely to have more objects and could wish to delete chunks of them, but you don’t anticipate grouped PUT, POST or PATCH requests.
If the limitation against PUT, POST, PATCH frustrates you there are some possible reasonable interpretations on those, just beware of assuming that nothing will change between your requests and the vulnerabilities that could appear if you allow PUT to create/update objects with a user-given id.
Create a specific grouped operation handler
When there doesn’t seem to be any correct and intuitive way for you to group the commands you wish to group you turn to the mightiest of VERBS: POST. The syntaxes that I’ve seen take a json list of operations (path, VERB and body) and returns a list of results for each of those. Some further implement a semi-templating feature that allows using data from one operation in another.
Advantages:
- unlimited potential: any VERB any grouping anything (you can define a turing complete domain specific language for your API and calculate pi)
- granular error reporting
- good performance
Disadvantages:
- requires significant documentation
- a lot more code
- encourages you to duplicate your API (be reasonable, use functions to minimize duplication)
- DoS risk (depending on features and rate limiting this could use a lot of server side performance)
Summary:
For more complex situations a separate handler could well be warranted, but it is not something to implement on a whim. That said, a minimal implementation (list of independent operations) is not prohibitively difficult to implement and is immediately more powerful than any other option.