There is another similar question to mine, but the discussion veered away from the problem I'm encounting.
Say I have a system that deals with expense reports (ER). You can create and edit them, add attachments, and approve/reject them.
An expense report might look like this:
GET /er/1
=>
{"title": "Trip to NY", "totalcost": "400 USD",
"comments": [
"john: Please add the total cost",
"mike: done, can you approve it now?"
],
"approvals": [
{"john": "Pending"}, {"finance-group": "Pending"}]
}
That looks fine, right? Thats what an expense report document looks like.
If you want to update it, you can do this:
POST /er/1
{"title": "Trip to NY 2010"}
If you want to approve it, you can do this:
POST /er/1/approval
{"approved": true}
But, what if you want to update the report and approve it at the same time? How do we do that? If you only wanted to approve, then doing a POST
to something like /er/1/approval
makes sense.
We could put a flag in the URL, POST /er/1?approve=1
, and send the data changes as the body, but that flag doesn't seem RESTful.
We could put special field to be submitted, too, but that seems a bit hacky, too. If we did that, then why not send up data with attributes like set_title
or add_to_cost
?
We could create a new resource for updating and approving, but (1) I can't think of how to name it without verbs, and (2) it doesn't seem right to name a resource based on what actions can be done to it (what happens if we add more actions?)
We could have an X-Approve: True|False header, but headers seem like the wrong tool for the job. It'd also be difficult to get set headers without using javascript in a browser.
We could use a custom media-type, application/approve+yes
, but that seems no better than creating a new resource.
We could create a temporary "batch operations" url, /er/1/batch/A
. The client then sends multiple requests, perhaps POST /er/1/batch/A
to update, then POST /er/1/batch/A/approval
to approve, then POST /er/1/batch/A/status
to end the batch. On the backend, the server queues up all the batch requests somewhere, then processes them in the same backend-transaction when it receives the "end batch processing" request. The downside with this is, obviously, that it introduces a lot of complexity.
So, what is a good, general way to solve the problem of performing multiple actions in a single request? General because its easy to imagine additional actions that might be done in the same request:
- Suppress or send notifications (to email, chat, another system, whatever)
- Override some validation (maximum cost, names of dinner attendees)
- Trigger backend workflow that doesn't have a representation in the document.
Its also an issue of performance. HTTP calls hit the network (which could be a problem if you have high latency or a flaky connection), so the fewer of them you can make, the better.