We have a big list ("collection") with a number of entities ("items"). This is all managed via a RESTful interface. The items are manually sortable via an order
property on the item. When queried, the database lists all items in a collection based on the order.
Now we want to expose this mechanism to users where they can update the complete sorting of all items in one call. The database does not allow the same order
for the same collection_id
(unique collection_id
+ order
), so you can't (and definitely shouldn't) update all items one by one.
I thought of a PATCH request but not on the resource, so
PATCH /collections/123/items/
With a body like
[
{'id': 1, 'order': 3},
{'id': 2, 'order': 1},
{'id': 3, 'order': 2}
]
However, how do you handle errors for this bulk-type of request? How do you send a response when some update succeeded partially? Is it allowed to PATCH a collection instead of a resource? If this is the wrong line of thought, what is a better approach?
First, answering your questions in the last paragraph:
How to handle errors in bulk requests depends a lot on the request. In your case, I think partial success should not be allowed and you should rollback the whole operation and return an error, as the only reason for a failure is someone dealing with an outdated representation. When you are creating or deleting resources in bulk, for instance, it's fine to accept a partial success.
You can handle errors in bulk requests by using the 207 Multi-Status
HTTP code. It's a WebDAV code, but it's pretty standard by now. The response should be a document with detailed HTTP status codes and messages for each item.
A collection is a resource too, so there's nothing inherently wrong in using PATCH
with a collection, but...
A PATCH
request should have some sort of diff format as payload, determining the state you want to transition from, and the final state. I wouldn't go with PATCH
for doing what you want unless you're willing to use a more standardized format. You might want to check the json-patch format, create a diff between the current and the desired order and see if you like the format. For instance, in your case it would be something like:
[{"path": "/0/order", "value": 1, "op": "test"},
{"path": "/0/order", "value": 2, "op": "replace"},
{"path": "/1/order", "value": 2, "op": "test"},
{"path": "/1/order", "value": 3, "op": "replace"},
{"path": "/2/order", "value": 3, "op": "test"},
{"path": "/2/order", "value": 1, "op": "replace"}]
Of course, if the client doesn't care about the current order, he can remove the test
operations. You might also add a precondition with the If-Unmodified-Since
or If-Match
header instead.
You probably noticed how the format above is completely generic and has no direct coupling to the resource you're changing. You can implement the above in a generic way and reuse that to implement PATCH
wherever you need it in your API.
Anyway, your case is simple enough that I would do it by having another resource with the order in a simple, flat format. Something like this, returning a list of ids in the current order:
GET /collections/123/items/ordering
[1, 2, 3]
And you can change the order by:
PUT /collections/123/items/ordering
[2, 3, 1]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With