Transactions in REST?

Viewed 87578

I'm wondering how you'd implement the following use-case in REST. Is it even possible to do without compromising the conceptual model?

Read or update multiple resources within the scope of a single transaction. For example, transfer $100 from Bob's bank account into John's account.

As far as I can tell, the only way to implement this is by cheating. You could POST to the resource associated with either John or Bob and carry out the entire operation using a single transaction. As far as I'm concerned this breaks the REST architecture because you're essentially tunneling an RPC call through POST instead of really operating on individual resources.

13 Answers

Consider a RESTful shopping basket scenario. The shopping basket is conceptually your transaction wrapper. In the same way that you can add multiple items to a shopping basket and then submit that basket to process the order, you can add Bob's account entry to the transaction wrapper and then Bill's account entry to the wrapper. When all the pieces are in place then you can POST/PUT the transaction wrapper with all the component pieces.

There are a few important cases that aren't answered by this question, which I think is too bad, because it has a high ranking on Google for the search terms :-)

Specifically, a nice propertly would be: If you POST twice (because some cache hiccupped in the intermediate) you should not transfer the amount twice.

To get to this, you create a transaction as an object. This could contain all the data you know already, and put the transaction in a pending state.

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

Once you have this transaction, you can commit it, something like:

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

Note that multiple puts don't matter at this point; even a GET on the txn would return the current state. Specifically, the second PUT would detect that the first was already in the appropriate state, and just return it -- or, if you try to put it into the "rolledback" state after it's already in "committed" state, you would get an error, and the actual committed transaction back.

As long as you talk to a single database, or a database with an integrated transaction monitor, this mechanism will actually work just fine. You might additionally introduce time-outs for transactions, which you could even express using Expires headers if you wanted to.

You'd have to roll your own "transaction id" type of tx management. So it would be 4 calls:

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

You'd have to handle the storing of the actions in a DB (if load balanced) or in memory or such, then handling commit, rollback, timeout.

Not really a RESTful day in the park.

I think that in this case it is totally acceptable to break the pure theory of REST in this situation. In any case, I don't think there is anything actually in REST that says you can't touch dependent objects in business cases that require it.

I really think it's not worth the extra hoops you would jump through to create a custom transaction manager, when you could just leverage the database to do it.

I guess you could include the TAN in the URL/resource:

  1. PUT /transaction to get the ID (e.g. "1")
  2. [PUT, GET, POST, whatever] /1/account/bob
  3. [PUT, GET, POST, whatever] /1/account/bill
  4. DELETE /transaction with ID 1

Just an idea.

Related