I'm looking for some best practices for "dry run" operations for REST APIs.
Say I have an endpoint that transfers money from account A to account B. I can initiate a transfer like so:
POST /transactions
{
"amount": 1000, // how much to transfer
"source": "A", // account to transfer from
"destination": "B" // account to transfer to
}
This action creates a "transaction", so the response would be a transaction object with payload containing:
{
"id": "txn-123",
"amount": 1000,
"source": "A",
"destination": "B",
"fees": 10, // any fees that were charged
"balance": 2500 // balance in source account AFTER transfer
}
I'd like to be able to perform a dry run for a couple reasons:
So, what is the best practice for a "dry run" concept? I can think of a few options:
These are my opinions on the matter so far, but I'd enjoy hearing other peoples' thoughts.
October 2020) Representational state transfer (REST) is a software architectural style that describes a uniform interface between physically separate components, often across the Internet in a Client-Server architecture. REST defines four interface constraints: Identification of resources. Manipulation of resources.
Option 3 is clearly wrong. You'd be forcing the client to do extra work for no good reason.
Choosing between options 1 and 2 is a matter of taste and the specifics of your API. It's not clear how reasonable it is to consider a /dry-run-transaction
to be a separate kind of thing than a transaction.
If you take option 2, consider making the /dry-run-transaction
a short-lived resource, or don't persist it at all. That way clients can POST to see fees and you save on storage space.
If you use option 1, I think the most technically correct option would be to include a property in the payload, such as execute: false
. This returns all the information to the client, and lets them do a PUT on the transaction with execute: true
to submit it exactly as it is. A disadvantage to this approach is you're going to have a bunch of non-executed transactions sitting around (forever?) because people are going to decide not to execute them after seeing the results.
I don't think safety comes into the discussion at all. If you're really worried about it, just make execute: false
the default for option 1.
Another approach might be to have endpoints /transactions
and /transaction-results
. If you post to /transaction
, you execute the transaction and get an id/result for a permanent transaction-result. If you POST to /transaction-results, you get a response with no id and a transient set of results for the transaction. Note I've thought about this for all of 20 seconds, so there might be a glaring problem with it I haven't seen. :-)
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