POST requests can be made idempotent by binding the result of processing that request to a future, so every subsequent request simply returns the already computed result.
Impure reads are the default, and pure reads are designated by long-lived cache headers, ie. it's an impure read that lasts as long as the server responds.
So really you just need GET and POST, and server-side frameworks should make POST requests idempotent by embedding some notion of futures for side-effecting operations. See the Waterken server for the first development platform to really get this right.
e.g. Instead of saying "Insert a transaction for £20", you say, "Insert a transaction that entity A calls 1234, for £20".
Stripe use something like this to ensure that as long as you call their API to ask for a payment and use the same key, they won't charge the person twice.
Not sure what you mean by client-generated. The server-side app generates the id because it has to store and interpret it. This can be as simple as a sequence number, similar to how TCP guarantees delivery. It depends on the schema really.
This is the simplest way to ensure you can perform arbitrary retries of POST in case of a network partition.
This data doesn't even need to be integrated with your app's schema, although that's ideal so you can manage the storage lifetime. But you could use an entirely separate store for GUIDs and cached replies, and so it becomes transparent to your app and just becomes another layer.
Per the description above:
"Each non-idempotent operation is tagged with a unique identifier"
Since the operation originates on the client, the client has to tag it with the identifier, no? And the server has to store this identifier in a way that associates it with any data affected by that operation in a non-idempotent way.
Or are you saying that the client first has to make a round-trip to the server to generate the ID, and then use that server-provided ID for the actual POST?
This is always the case for REST given HATEOAS, ie. you've already made some hypermedia requests to obtain the URL of the endpoint to which you will POST.
Unless the resource you're posting to actually is the public entry point of your service, but that would be very unusual.
Generally speaking, what is the flow like? Suppose I allocated myself an endpoint, but then never posted anything to it - what does the endpoint actually contain then, if queried? Do unused ones get "garbage collected" somehow eventually?
Not necessarily. The numbers don't have to be stored before they're actually used by clients. For instance, you could return a simple integer, like an object version #, and an HMAC(integer, resource URL) to ensure the client can't tamper with it. You only store data under that integer when the user successfully POSTs to that resource for the first time.
> Generally speaking, what is the flow like? Suppose I allocated myself an endpoint, but then never posted anything to it - what does the endpoint actually contain then, if queried?
There are many possible designs here. I prefer something like this in CRUD-like contexts [1], because it gets me a fully auditable change history, optimistic concurrency control and all the storage needed is fully integrated into the app schema in a sensible way.
Combined with the HMAC described above, you don't need to preemptively allocate any storage for idempotent POSTS and you can use simple integers as your unique identifiers. So a POST against a version number that's not the latest version would simply return the version that followed if their POST was the one that succeeded, or a redirect to the latest version if someone else had updated it.
Hopefully you can imagine various relaxations on this to make other trade offs. For instance, a much simpler approach would be to return GUIDs for each possible POST, and you just store each GUID associated with the object when a POST succeeds. If the object's current GUID is the GUID the user is posting, return the current data with 200 OK, otherwise return a 301 Moved to a URL referencing the latest version and let the client try to apply their updates to that. The storage can be reclaimed after a suitable period of time; say a month if your app is for browser clients
[1] https://higherlogics.blogspot.ca/2015/10/versioning-domain-e...
By "pure" here I mean that we're talking about a pure function of its inputs.
Why distinguish? Because if you know that the results are not affected by server state, they can be cached much more aggressively.
1. Pure read.
2. Impure (stateful) read.
3. Idempotent write.
4. Any other write.
There's no particular reason to separate inserts, updates, deletes etc as part of the protocol - they're all just different kinds of writes, and middleware doesn't derive any benefit from being able to distinguish them. Thus, this can be a part of the payload.
On the other hand, the difference between reads and writes, and the two subdivisions within each, do matter for purposes such as caching and automated error recovery (e.g. a proxy can repeat an idempotent write a few times before returning the error to the originator of the request).
In REST, we have GET for #1 and #2, PUT and DELETE for #3, and POST for #4. In practice, this is often simplified to just POST used for both #3 and #4, but that loses a valuable distinction (but is unfortunately often necessary because of the lack of support for other methods). On the other hand, the PUT/DELETE distinction is largely pointless.