2

Rest without PUT models immutable, append-only (event-sourced) state changes by favouring POST-ing new reified change resources over PUT-ing updates to original. Example: making and then changing a coffee order...

Traditional REST:

  • POST /order {type: latte} → 201 Created /order/1234
  • PUT /order/1234 {type: mocha} → 200 OK

REST without PUT:

  • POST /order {type: latte} → 201 Created /order/1234
  • POST /order/1234/change-req {type: mocha} → 201 Created /order/1234/change-req/5678

Question: Can this work with optimistic locking using Etags and If-Match?

Traditional REST:

  • POST /order {type: latte} → 201 Created /order/1234, ETag: "1111"
  • PUT /order/1234 {status: delivered} If-Match "1111" → 200 OK ETag "2222" (barista is quick!)
  • PUT /order/1234 {type: mocha} If-Match "1111" → 412 Precondition failed

REST without PUT:

  • POST /order {type: latte} → 201 Created /order/1234, ETag: "1111"
  • POST /order/1234/deliver-req {} If-Match "1111" → 201 Created /order/1234/deliver-req/9012
  • POST /order/1234/change-req {type: mocha} If-Match "1111" → 412 Precondition failed

Does it make sense to allow the Etags sent and matched to refer to the original order resource, and not to the state of the reified changes resources (deliver-req, change-req) themselves?

  • 1
    I honestly don't get the rest obsession with squeezing meaning into HTTP codes. what wrong with POST /changeOrder { id:1234, to : mocha } 200 {error: unable to change order because its locked} – Ewan Nov 16 '15 at 22:27
  • 1
    It's a uniform interface that is implemented / documented everywhere which already implements this pattern. Why not use it? – Anthony Leonard Nov 16 '15 at 23:02
  • its not though is it. Everyone has their own view on whether operation X should be post/get/put/delete/option etc etc some methods aren't even uniformly supported let alone used – Ewan Nov 19 '15 at 23:59
  • @AnthonyLeonard did you reach any conclusion about your last question? "Does it make sense to allow the Etags sent and matched to refer to the original order resource, and not to the state of the reified changes resources (deliver-req, change-req) themselves?" – Guilherme Ferreira Oct 25 '17 at 16:14
3

Update: Can etags + If-Match work? Yes, I'm sure it can be made to work. Your client will have to interpret the 412 error as an optimistic concurrency failure. As far as using it with POST, the spec does not restrict which methods it is allowed on.

The If-Match request-header field is used with a method to make it conditional.

I would also return 412 (with a different message) if there is no entity (and therefore no etag) found in the database. In other words, they are requesting modification for an entity that was never created.

The question remains as to whether you should use If-Match for optimistic concurrency. The main problem with it is that semantically, it allows the client to specify whether or not the resource should be updated. It is very possible that the business will want some optimistic concurrency failures to be allowed to continue anyway. F.ex. if CancelOrder is received, regardless of version mismatch, it should be processed. (It could be that the order is already shipped and cancellation fails, but at least we can respond with that information instead of just a version mismatch error!) This is not a decision you usually want on the client. However, you could subvert the spec and just use If-Match to send your version (e-tag), and ultimately let the business rules decide whether the operation should continue if the tags don't match and return the 412 otherwise.

Should you send the etag to the reified URI? Yes. In fact as part of modeling what is essentially messaging over HTTP, there should be no POST URI for an "original order resource". What you are really doing with that order URI is sending the "create order" command message. If you changed its name to fit that, then that would make it much more clear that POSTing to an Order resource doesn't make sense. In this type of API the POST URIs are message types, not resources.

By the way, I wholeheartedly disagree with the linked article at making these nouns instead of verbs. REST tries to represent operations (actions) as HTTP verbs, which fits fine for the 4 CRUD operations, but is far too limited for domain operations. Realizing this, the article's author moves the domain operation to be part of the URI, but tries to retain the REST principle of noun URIs (I assume) to stick to the REST spec in letter, even though it is not followed in spirit. Sometimes you just have to acknowledge that something doesn't fit and don't force it. Verbs definitionally include actions, and actions more closely represent business operations. Therefore, use POST /create-customer instead of POST /create-customer-req. Do you really want to add a superfluous -req on the end of every URI?

The read side
It makes complete sense to have a GET URI for Order. Actually, it makes sense to have multiple GET URIs for Order, one for each different perspective (view) on the order. F.ex. a customer is likely to want different information when looking at their orders than the sales manager. (This may actually lead to different bounded contexts, but that is a different discussion.)

Update

I see your edit changed the URIs to the form POST order/123/action instead of including the ID in the JSON body. Either way, this does not make sense in the context of REST. The URI is supposed to represent a resource, but here is a message type. The fact that you are communicating an ID via URI doesn't make it a resource. The same way that changing an action to a noun form doesn't make it a resource. Sticking to REST naming conventions without actually implementing a uniform interface is in no way helpful. The idea of following REST is to use HTTP in a standard and consistent way so that clients have a reasonable expectation for how it works.

In the case of a CRUD resource conforming to REST, if you gave the client a URI and object shape, it would already know how to perform all operations. In your case, the clients don't have knowledge of your custom operations until you tell them (via hypermedia perhaps, or hard-coding in the client).

Let me re-emphasize: It's okay that your use case doesn't fit in the REST box! It's okay to use HTTP in a different way that makes sense for your system!

  • Made a slight edit which hopefully doesn't alter this great answer :) However I'm still looking for more assurance that it's "ok" to do a conditional (If-Match) POST of a change-req resource such that it succeeds only if the ETag sent matches the ETag of its parent order resource... – Anthony Leonard Nov 16 '15 at 22:55
  • @AnthonyLeonard. I updated my answer, reflecting your edit, and the okay-ness of if-match POST. – Kasey Speakman Nov 18 '15 at 21:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.