REST subresources as commands

Posted on March 20, 2016 · 1 min read · tagged with: #REST #subresource

This post is written write after reading the @liveweird entry about How not to hurt yourself while building a RESTful API. If you don’t know it, it’s probably a good time to read it.

Sebastian mentions that POSTs can be used for any non-crud like operation. I agree with that as the POSTs have a notion of general commands, they are not idempontent and they actually cause a change in a resource. They’re much smarter than DELETEs (just trash it) or PUTs (just replace it with my content). How do you distinguish different operations though? Is a header a good place to put the name? Or maybe a property in a JSON payload?

Subresources to the rescue

The easiest way to model actual non-CRUD commands I’ve found so far is modelling th e commands as subresources specialized in handling them. For example consider the following resource:

/car/1

One could argue how to model operations like rent, return, etc. My take on this would be POSTs against the following subresources:

/car/1/rent

/car/1/return

This modelling is easy to navigate (either you want to HATEOAS or simply Swagger your API) and easy to follow. It has an additional advantage as well. It’s very easy to bind specific commands to an MVC/WebAPI controller methods as every operation can be handle with a separate method. Even if you write a custom class for each of this operations (a command object), leveraging this, can spare you some time on writing your own routing/dispatching of the request.


Comments

One of the most important principles behind REST is the Uniform Interface (http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5). This means that the API should be totally separated from the underlying implementation style. This is what enables the API evolvability.

I'm just thinking out loud here. Exposing the notion of command at the resource level might be considered as leaking the implementation style details. What would be an expected response for GET /car/1/rent? What if we decided that CQRS is an overkill and changed the implementation style? Would the notion of command still be applicable and worth to expose at the API level in such a case?

How about an API like this:

GET /cars

GET /car/1

{

"model": "BMW",

"color": "red",

"links": [

{

"rel": "rent",

"href": "/cars/rented",

"method": "PUT",

"properties": {

"carId": {

"title": "car identifier",

"type": "string"

},

"driverId": {

"title": "Driving license id",

"type": "string"

}

},

"required": [

"carId",

"driverId"

]

}

]

}

GET /cars/rented

PUT /cars/rented

{

carId: "666",

driverId: "AAA00000",

}

GET /cars/available-for-rent

PUT /cars/available-for-rent

{

carId: 666,

}

Yay or nay?

DISCLAIMER: Of course there is no single, correct design. The discussion here is more about conforming to the REST constraints vs. taking shortcuts and being pragmatic :)

by Krzysztof Sadowski at 2016-03-25 09:54:30 +0000

Subresources may be handy but they are just an implementation detail. There is just one very important bit missing in what you write. Clients should be implemented not by working with the URI like

var carUrl = '/car/1';

var rentUrl = carUrl + '/rent';

And thet POSTing or whatever to the rentUrl endpoint. This way is brittle, introduces coupling between the client and ther server, and closes your path to flexible evolution. For example what if you needed different URLs to do the rent operation? You would have to code all that logic in the client.

What you should be doing is following links (and operations are links in a sense albeit requiring request body and non-GET method). And so your model should at least include the actual operation URLs and the client be hardcoded against link relations. For example

{

"id": "/car/1",

"rentUrl": "/car/1/rent",

"returnUrl": "/car/1/return"

}

Now the client simply POSTs to a given URL, which comes from the server. This opens a whole spectrum of possibilities. The client immediately "knows" that it cannot rent a car if the property is missing. Or the server could provide different URLs to different users - for example by prioritizing "gold members", etc.

by Tomasz Pluskiewicz (@tpluscode) at 2016-03-20 18:53:45 +0000

Thank you Tomasz, I was waiting for your comments:)

I wouldn't call them just an implementation detail. They truly enable you to provide all the needed well-described operations operations.

The approach you're describing is a step forward to the hypermedia driven approach, isn't it? As usual, one may raise a question how one can the client get semantics behind these two properties. Is it even possible to get this agnostic client just following the hypermedia? Even if one doesn't want to chase this agnostic client dream, I do agree that including hypermedia can help you address some concerns.

by Szymon Kulec 'Scooletz' at 2016-03-20 19:27:41 +0000