POST vs PATCH for canceling an order

155 views
Skip to first unread message

Wouter Lansu

unread,
Aug 20, 2015, 4:47:27 AM8/20/15
to API Craft
I recently came across this group and decided to call in your expertise for a matter we've been struggling with.

We have a RESTful API where you can create orders in our system. The only change someone can do, at the moment, is canceling an order he has created. Canceling an order does not delete it, nor does it make it un-discoverable in the API, it marks the order as canceled and internally we make some changes so any products become available again for purchase.

What we're struggling with is how to create this call, should it be a POST or a PATCH?

I would love to hear your thoughts!

If anything is unclear please don't hesitate to ask questions.

Jørn Wildt

unread,
Aug 20, 2015, 5:20:30 AM8/20/15
to api-...@googlegroups.com
I have been struggling with this kind of decisions too and ended up with the following standard approach:

- Use PATCH for changing one or more unrelated properties with no large-ish side effects. Like for instance modifying the title of a blog post or changing the severity of a bug report.

- Use POST to a "processor" resource for more complex business operations such as cancelling an order or a ticket, shipping an order or hiring a new employee.

- Use GET on the "processor" resource to get current state of the process and/or a description/documentation of the process and/or hypermedia controls informing the client about how to interact with the process.

Typically I use a "sub-resource" URL for the processing. For instance:

  GET /orders/1234 => return order

  POST /orders/1234/cancellation => cancel order

  GET /orders/1234/cancellation => get description of cancellation process, hypermedia controls or what ever that describes the operation

  POST /orders/1234/shipping => ship order

  GET /orders/1234/shipping => get shipping state and/or description of process.

  GET /job-positions/1234 => return open job position

  POST /job-positions/1234/hire => hire new employee (pass in employee details or reference to existing applicant)

/Jørn




--
You received this message because you are subscribed to the Google Groups "API Craft" group.
To unsubscribe from this group and stop receiving emails from it, send an email to api-craft+...@googlegroups.com.
Visit this group at http://groups.google.com/group/api-craft.
For more options, visit https://groups.google.com/d/optout.

mca

unread,
Aug 20, 2015, 11:22:02 AM8/20/15
to api-...@googlegroups.com
i rarely get tied up on the "application-level" meaning of a POST or PATCH. instead i focus on the "network-level" meanings:

for HTTP you have:
GET: safe, idempotent
POST: unsafe, non-idempotent (URL is created by the service) [appends to an existing resource collection]
PUT: unsafe, idempotent (URL is supplied by the client) [replaces existing resource state]
PATCH: unsafe, non-idempotent (URL [of existing resource] is supplied by the client) [amends existing resource state]
DELETE: unsafe, idempotent [removes an existing resource]

with this information, you can match the network promise w/ your local application needs. 
- Need a way for clients to create new resources even when they don't know the URL of that new resource? that's POST. 
- Need a way to edit just portions of an existing resource? that's PATCH.
- Need a way for clients to create new resources in a way that is safely repeatable in case of loss of signal? that's PUT.
- Need a way to replace an existing resource state? that's PUT.

and so forth....

by thinking of the methods in this way (safe/unsafe, idempotent/non-idempotent, replace/append/amend/remove) you also make it possible to apply the same thinking to multiple protocols (XMPP, MQTT, CoAP, etc.) which can add flexibility to both your implementation and your design.

cheers.




Johan Groenen

unread,
Aug 20, 2015, 11:29:38 AM8/20/15
to API Craft
Since you are updating an attribute of an existing resource, I would say you need a PATCH on the resource URI.

You are free to choose your flavor of PATCH (read this, and then make your decision: http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/).

Cheers!

Op donderdag 20 augustus 2015 10:47:27 UTC+2 schreef Wouter Lansu:

Ryan Hiebert

unread,
Aug 20, 2015, 11:47:56 AM8/20/15
to api-...@googlegroups.com

> On Aug 20, 2015, at 10:29 AM, Johan Groenen <johan....@gmail.com> wrote:
>
> Since you are updating an attribute of an existing resource, I would say you need a PATCH on the resource URI.
>
> You are free to choose your flavor of PATCH (read this, and then make your decision: http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/).

Thank you so much for this. It brings much-needed clarity to a problem I've been working on.

Stephen Bartell

unread,
Aug 20, 2015, 1:57:26 PM8/20/15
to API Craft
Ive solved it both ways, plus an additional way, for a number of routes I maintain.

PUT
I hang a bool property off the representation, `cancelled` or `deleted`.  Flip the bool to false and PUT it back.  PUT /orders/12345

POST
per the spec allows partial updates.  Do the same as the PUT scenario, except partial POST it back.  POST /orders/12345

PATCH
the same as the above two, except it's a patch payload describing only the property which has changed.


Less concretely to your question, here's my take from experience on each of these approaches.  I'm just going to brain dump some thoughts/questions I've had from the field

PATCH
Its awesome to send just what you need.  Very message oriented.  There are a few gotchas with patch though.
1. elements of arrays are no longer addressable by their id. Instead, an elements ordinality becomes the id.  This drives me completely insane.  Unless you do something like Netflix's Falcor where arrays are represented as objects, there's no getting around it unless a different flavor of patch is used.  At which point you're requiring your clients to account for yet another media format.
2. Hints that my representation (aggregate) is two big.  Can I break it up?  Do I really need a transaction around the whole thing?  Can I make separate routes for this?  If its not too big, maybe just a few properties, then PUT would work fine.

POST
I just loath partial updates because they force you to write some gnarly code.  What does the absence of a property mean; null-out or no change?  You need to account for each property in the representation.  Can't just whack the whole thing straight into the database ... unless of course that's your design.

PUT
The cleanest imho.  Just flip the bit and send the whole thing back.  Getting the cancellations is easy without polluting the uri space by using query params - /orders?cancelled=true


I like mca's network-level meanings too.


hope that helps

Kijana Woodard

unread,
Aug 20, 2015, 2:49:36 PM8/20/15
to api-...@googlegroups.com
I'm partial to Jørn's sub-resource formulation. 

For PATCH & PUT, back-solving for intent is difficult. 
If you're doing CRUD, this doesn't matter. 
If you need contextual downstream processing, I'd rather have dedicated endpoints that trying to diff state to divine intent.

--

Cooper Marcus

unread,
Aug 20, 2015, 3:06:17 PM8/20/15
to api-...@googlegroups.com
I've found this page http://restcookbook.com/HTTP%20Methods/idempotency/ to be very helpful in answer the sorts of questions posed by the original poster.

Wouter Lansu

unread,
Aug 21, 2015, 4:40:50 AM8/21/15
to API Craft
First of all I'd like to thank everyone for their contributions so far. There's been some very insightful replies and especially some helpful links provided.

As I read more about this subject I'm leaning towards PATCH, it seems to provide everything we need at the moment and makes it very clear in the verb itself what the intention is. And it also leaves room for expansion in the future.

Having said that, I found two separate RFC's for PATCH. Json-patch and Json-merge. Merge seems a great deal simpler and consumers of API's like simple things, or at least I do. Do any of you care to weigh in on this?

Jørn Wildt

unread,
Aug 21, 2015, 5:22:33 AM8/21/15
to api-...@googlegroups.com
As I read more about this subject I'm leaning towards PATCH, it seems to provide everything we need at the moment and makes it very clear in the verb itself what the intention is

Allow me to disagree here: if you PATCH multiple properties at the same time is it then clear what is going on? If the client *only* patches "canceled = true" then the server should be able to figure out the intent ... but what if the client patches *both* "canceled = true" and "shipped = true" at the same time?

Patching is somewhat "assembly language" for APIs - you *can* reverse engineer the original program (client intention) - but some details are lost. It also moves some unnecessary business knowledge to the client, making it difficult to update the server later on. With PATCH the client must know exactly what properties to change in order to perform a business operation such as cancel - also those that should be changed by the server only as some internal side effect of the operation.

Another problem with patching is that you cannot supply additional arguments that are not part of the patched resource. This can for instance be a logging statement that should be included with the cancellation or a reference to the user that performs the cancellation.

So, in short, by using PATCH you put more burden on the client, reduce the ability to include related information and hide the original intent of the operation from the server.

Things will work with PATCH as long as you only have a single "cancelled" bit to swap (simple CRUD operation) - but it may cause you some pain later on when the cancellation process becomes more complex. 

/Jørn





--
Reply all
Reply to author
Forward
0 new messages