Junior Developer
Junior Developer

Reputation: 101

REST API with multiple commands per resource

I have a question regarding REST API design. Here is a simple (maybe too simple) API:

GET /ecommerce/order/123

POST /ecommerce/order (create a new order)

PUT /ecommerce/order/123 (update an existing order)

DELETE /ecommerce/order/123 (cancel order)

But what if I wanted the customer to enter a reason for an order to be cancelled? I would need to send post data to the API, but that wont work with DELETE. To cater for this the I would have to change DELETE to PUT. I would then post two different resources for update and cancel.

Another solution would be to change the API:

GET /ecommerce/order/123

POST /ecommerce/order/create (create a new order)

PUT /ecommerce/order/update/123 (update an existing order)

DELETE /ecommerce/order/cancel/123 (cancel order)

I'm not sure which is the best option.

There is a more general question about how REST API's handle multiple commands for a single resource.

Any input would be appreciated! I'm going to be reading REST in Practice very soon but this question is niggling away at me.

Upvotes: 10

Views: 4985

Answers (3)

Thomas Emil Hansen
Thomas Emil Hansen

Reputation: 41

I know this is a very late answer, but I suggest to use the first set of commands, but change the cancel order command to:

POST /ecommerce/order/123/cancel

Which is a generic way to handle various operations on an existing resource. I don't see why an order cancellation would lead to deletion of the order itself, at least not instantly. The users would probably still want to see the order in the system. The order is also where you would store the reason for the cancellation.

Upvotes: 4

drdaeman
drdaeman

Reputation: 11481

(The question is quite old, but I just thought I'd improve it, as I don't like any solutions out there.)

The solution is simple. Put a reason into the request's body.

DELETE /ecommerce/order/123
Content-Type: text/plain
Content-Length: 48

Order was cancelled due to a customer's request.

The semantics of the body is up to you to decide. If you only want a plaintext reason, I'd use text/plain as shown above. If more complicated metadata is required, I'd complicate things further.

In my opinion this is way better than Javaesque RPC-like OrderCancellator.execute(order) (because POST is HTTP's name for "execute").

Beware, that while spec doesn't say a thing about it, some severs may discard DELETE request's body. A draft on HTTP/1.1 message semantics clarifies:

Bodies on DELETE requests have no defined semantics. Note that sending a body on a DELETE request might cause some existing implementations to reject the request.

Another option is to issue a header:

DELETE /ecommerce/order/123
X-Reason: Cancelled due to an UFO invasion.

Whenever to choose headers or entity body depends on size and format of the data. Some data fits fine in HTTP headers, some does not. One certainly can pass numeric ticket ID, it's uncertain about strings (think Unicode) and it's certain nobody in their sane mind wants to pass a Base64 JPEG there.

Upvotes: 2

Rob Hruska
Rob Hruska

Reputation: 120306

One option might be to create a new resource. CancelledOrder, perhaps.

You might then POST a new CancelledOrder:

POST /ecommerce/cancelledOrder
Entity:
    order: /ecommerce/order/123
    reason: "Problem with order"

You could also/instead PUT a CancelledOrder:

PUT /ecommerce/cancelledOrder/123
Entity:
    reason "Problem with order"

The application could then delete order 123 or update its status to "Cancelled", or do whatever it is your business rules require. To top it off, you could then not support the DELETE method directly for /ecommerce/order/N, returning a 405 Method Not Allowed.

The PUT solution can use idempotence to its advantage; PUTting the CancelledOrder multiple times would always still result in the order being cancelled.

It should be noted that your suggestions for changing the API (e.g. /ecommerce/order/create) are likely not to be RESTful, since you're defining methods in the resource identifiers.

Upvotes: 7

Related Questions