108

What is the "RESTful" way of adding non-CRUD operations to a RESTful service? Say I have a service that allows CRUD access to records like this:

GET /api/car/123           <- Returns information for the Car object with ID 123
POST /api/car              <- Creates a new car (with properties in the request)
PUT /api/car/123           <- Updates car 123 (with properties in the request)
DELETE /api/car/123        <- Deletes car 123    
POST /api/car/123/wheel/   <- Creates a wheel and associates it to car 123

If I want to change the car's color, I would simply POST /api/car/123 and include a POST variable for the new color.

But let's say I want to purchase a car, and that operation is more complicated than simply updating a "user" record's "owned car" property. Is it RESTful to simply do something like POST /api/car/123/purchase, where "purchase" is essentially a method name? Or should I use a custom HTTP verb, like PURCHASE instead of POST?

Or are non-CRUD operations completely outside the scope of REST?

MikeWyatt
  • 7,692
  • 10
  • 44
  • 69
  • 5
    If you are changing a car's colour, it would be better to use `PATCH /api/car/123` and send a colour parameter OR use `PUT /api/car/123` and send the entire car object. POST would infer that you are creating a new car and should probably never include an id at the end of the URL – RonnyKnoxville Jan 12 '17 at 11:10

4 Answers4

66

Think about purchase as a business entity or a resource in RESTful dictionary. That being said, making a purchase is actually creating a new resource. So:

POST /api/purchase

will place a new order. The details (user, car, etc.) should be referenced by id (or URI) inside the contents sent to this address.

It doesn't matter that ordering a car is not just a simple INSERT in the database. Actually, REST is not about exposing your database tables as CRUD operations. From logical point of view you are creating an order (purchase), but the server side is free to do as many processing steps as it wants.

You can even abuse HTTP protocol even further. Use Location header to return a link to newly created order, carefully choose HTTP response codes to inform users about problems (server- or client-side), etc.

whoan
  • 7,411
  • 4
  • 35
  • 46
Tomasz Nurkiewicz
  • 311,858
  • 65
  • 665
  • 652
  • What if I don't want to treat an operation (e.g. a purchase) as a resource? Am I going completely against the RESTful methodology? – MikeWyatt Jul 27 '11 at 20:07
  • 3
    REST is all about manipulating the state of resources and every business operation has to be mapped to state CRUD operations. If you need *hard* business operations semantics, you'll have to go the SOAP way (SOAP is actually message passing, but is typically organized in request-response operations). – Tomasz Nurkiewicz Jul 27 '11 at 20:35
  • 23
    The "purchase as resource" design looks neat. What if the resource is a "beer".. and I want the server to drink it.. ( it it was for me, I'd surely GET it ;) ).. should we consider the "drink action" as a resource ?!.. or is "drinking a beer", a _hard_ business operation ?! More seriously, is the RESTful design about considering actions as resources ?!.. – Myobis Feb 10 '13 at 22:21
  • This is certainly the best way to model a purchase. Though Tomasz neglects to mention the benefits with respect to idempotence (creating a purchase order can be made obligation free, committing the purchase order is the risky operation, which can be done with an idempotent method like PUT). The same model can be used for a range of concepts, but there is no axiom that states that REST == CRUD. If it makes sense, [just use POST](http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post). – Martin Thomson Jul 03 '13 at 22:29
  • 2
    How would you expose "approve purchase order" through a REST service? I think @TomaszNurkiewicz is right in that anything that cannot be neatly done in a CRUD way will need the operation-semantics provided by SOAP. Unless "purchase order approval" is a model/entity on it's own. E.g. POST /po-approval (with PO details in the request). – mydoghasworms Feb 14 '14 at 06:22
  • 2
    From the viewpoint of a REST client "approve purchase order" should just be another update of the order. E.g. change "Approved" to "true" and send the update to the server. The server will probably need to do a bunch of checks and probably it will need to update/create a bunch of other resources. But that's the servers problem and shouldn't be visible to the client. – AVee Jun 25 '14 at 08:19
  • @AVee - Some potential objections to this approach... (1) The server may need to change other fields on the purchase order, that the client could not have predicted, breaking the semantics of "I am updating this resource to this particular value". (2) Suppose I have other possibilities in addition to "approve", such as "finalize", "decline", "backorder". Now, the code on the server that handles UPDATE action is a big multi-purpose thing with a lot of 'if's, instead of "do one thing well". (3) I don't like re-sending the entire resource if I don't need to - what if it's really big? – antinome Aug 11 '14 at 15:53
  • 1
    @antinome - 1, the server may always do things the client didn't predict. A big part of REST is about having a client being able to deal with the resource without knowledge about the logic behind the resource. If the client should depends on knowledge of what the server may or may not do it's not REST. (Also, other clients may do updates on the same resource.) 2, that should be fix with proper software engineering, but it should be the servers problem. Nothing stops from splitting the update into separate (atomic) steps. 3, Agreed, but that's what the PATCH verb is for. – AVee Aug 25 '14 at 13:34
  • 1
    @AVee: 3, I agree. 1 & 2: Sometimes, conceptually, what you are doing is updating a resource to a new state and some side effects happen incidentally. In such cases I agree with your approach. On the other hand, suppose there are multiple other resources being updated. Suppose emails are being sent. Suppose the client knows some of this and passes additional parameters to influence these things. Now, I would argue, the essence of what is happening is *not* an update to a single resource, and representing it as such would make the API harder to understand and reason about, without any benefit. – antinome Aug 25 '14 at 19:37
  • 2
    @antinome: "Suppose the client knows some of this", if that's the case you're not doing REST (it might still be valid, sensible software though!). REST was designed to be able to create clients which don't know that sort of thing, to create clients which still work if the behavior of the server changes. What you're trying to do is classic RPC, you either need to review your approach so it will fit REST, or accept that you are doing RPC and use a protocol intended for RPC like SOAP. REST tries very hard not to be RPC, so it's never going to be a good fit when you want/need RPC. – AVee Aug 26 '14 at 11:59
15

The RESTful way as I understand it is that you don't need new HTTP verbs, there's a noun somewhere that will mean what you need to do.

Purchase a car? Well isn't that

POST /api/order
djna
  • 52,574
  • 11
  • 70
  • 109
  • 2
    Isn't PUT used to *update* resources since it's idempotent? This means you can call it as many times as you want, but only the first/last call is important. POST on the other hand is used to *create* resources and calling it twice should actually create two. – Tomasz Nurkiewicz Jul 27 '11 at 19:46
  • 1
    @Tomas, yes, typo. Principle is important though, we're dealing with a new thing, an order, no need for a new verb. – djna Jul 27 '11 at 20:11
5

What you're really doing is creating an order. So add another resource for order and post and put there during the order process.

Think in terms of resources rather than method calls.

To finalize the order you'd probably POST /api/order//complete or something similar.

Andrew Kothmann
  • 572
  • 2
  • 4
3

I feel REST APIs help in lot more ways than just providing semantics. So cannot choose RPC style just because of some calls that seem to make more sense in RPC operation style. Example is the google maps api to find directions between two places. Looks like this: http://maps.googleapis.com/maps/api/directions/json?origin=Jakkur&destination=Hebbal

They could have called it "findDirections" (verb) and treated it as an operation. Rather they made "direction" (noun) as a resource and treated finding directions as a query on the directions resource (Though internally there could be no real resource called direction and it could be implemented by business logic to find directions based on params).

Maruthi
  • 411
  • 4
  • 11
  • 1
    That's a bad example. In this case the directions (all possible directions, infinite number of them) is the resource and the parameters are just filters. But you can't do an "purchase" with this, as filters only makes sense on get operations and putting an order or cancel it are operations that change the data – Tseng Oct 09 '15 at 18:57
  • 2
    purchase would be POST to /order with a json in the body to indicate an order is created. cancel would be PUT to /order with a json carrying the order state change to indicate this is an idempotent update. I am still to run into an operation that cant be expressed in a resource format. So would be keen to see an example like that – Maruthi Nov 04 '15 at 07:52