Readers new to this topic will be struck by the endless discussion about what you should do, and the relative absence of lessons from experience. The fact that REST is "preferred" over SOAP is, I suppose, a high-level learning from experience, but goodness we must have progressed from there? It's 2016. Roy's dissertation was in 2000. What have we developed? Was it fun? Was it easy to integrate with? To support? Will it handle the rise of smartphones and flaky mobile connections?
According to ME, real-life networks are unreliable. Requests timeout. Connections are reset. Networks go down for hours or days at a time. Trains go into tunnels with mobile users aboard. For any given request (as occasionally acknowledged in all this discussion) the request can fall in the water on its way, or the response can fall in the water on its way back. In these conditions, issuing PUT, POST and DELETE requests directly against substantive resources has always struck me as a little brutal and naive.
HTTP does nothing to ensure reliable completion of the request-response, and that's just fine because this is properly the job of network-aware applications. Developing such an application, you can jump through hoops to use PUT instead of POST, then more hoops to give a certain kind of error on the server if you detect duplicate requests. Back at the client, you then have to jump through hoops to interpret these errors, refetch, revalidate and repost.
Or you can do this: consider your unsafe requests as ephemeral single-user resources (let's call them actions). Clients request a new "action" on a substantive resource with an empty POST to the resource. POST will be used only for this. Once safely in possession of the URI of the freshly minted action, the client PUTs the unsafe request to the action URI, not the target resource. Resolving the action and updating the "real" resource is properly the job of your API, and is here decoupled from the unreliable network.
The server does the business, returns the response and stores it against the agreed action URI. If anything goes wrong, the client repeats the request (natural behaviour!), and if the server has already seen it, it repeats the stored response and does nothing else.
You will quickly spot the similarity with promises: we create and return the placeholder for the result before doing anything. Also like a promise, an action can succeed or fail one time, but its result can be fetched repeatedly.
Best of all, we give sending and receiving applications a chance to link the uniquely identified action to uniqueness in their respective environments. And we can start to demand, and enforce!, responsible behaviour from clients: repeat your requests as much as you like, but don't go generating a new action until you're in possession of a definitive result from the existing one.
As such, numerous thorny problems go away. Repeated insert requests won't create duplicates, and we don't create the real resource until we're in possession of the data. (database columns can stay not-nullable). Repeated update requests won't hit incompatible states and won't overwrite subsequent changes. Clients can (re)fetch and seamlessy process the original confirmation for whatever reason (client crashed, response went missing, etc.).
Successive delete requests can see and process the original confirmation, without hitting a 404 error. If things take longer than expected, we can respond provisionally, and we have a place where the client can check back for the definitive result. The nicest part of this pattern is its Kung-Fu (Panda) property. We take a weakness, the propensity for clients to repeat a request any time they don't understand the response, and turn it into a strength :-)
Before telling me this is not RESTful, please consider the numerous ways in which REST principles are respected. Clients don't construct URLs. The API stays discoverable, albeit with a little change in semantics. HTTP verbs are used appropriately. If you think this is a huge change to implement, I can tell you from experience that it's not.
If you think you'll have huge amounts of data to store, let's talk volumes: a typical update confirmation is a fraction of a kilobyte. HTTP currently gives you a minute or two to respond definitively. Even if you only store actions for a week, clients have ample chance to catch up. If you have very high volumes, you may want a dedicated acid-compliant key value store, or an in-memory solution.