What to do when you need more verbs in REST

Posted by Richard Levasseur on Stack Overflow See other posts from Stack Overflow or by Richard Levasseur
Published on 2010-04-22T02:32:45Z Indexed on 2010/04/22 2:33 UTC
Read the original article Hit count: 352

Filed under:
|

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:

  1. Suppress or send notifications (to email, chat, another system, whatever)
  2. Override some validation (maximum cost, names of dinner attendees)
  3. Trigger backend workflow that doesn't have a representation in the document.

© Stack Overflow or respective owner

Related posts about rest

Related posts about web-services