A proposal for REST and Verbs

Be warned, I’ve put this in the “opinionizing” category 😉

Every REST project that I’ve been on has put verbs in API URLs. Occasionally I can convince the team to change them to a processor noun, but about half the time I don’t do that. Part of that is because I don’t want to burn up my political capital on a silly issue like that.

So on my drive home, I got to thinking about verbs and REST. Here is my thinking about REST and Verbs.

First, let’s face it: if we’re just going to change the verb to a processor noun and use that instead, that’s just playing word games to conform to some silly social standard. Next we’ll all be wearing pink sweaters when we code because some expert told us it’s all the rage this season.

So what, really, is the problem with verbs and REST?

Let’s set aside the argument that goes, “because no.”

The problem is that verbs are by their nature are transient. They are abstract, and don’t have as lasting a nature as nouns. I can run, stop, walk, skip, dance around, and despite all those verbs, I am still me after all.

So verbs create a problem for PUT. Let’s say that I create a URL that includes a command to “run”. If I do this:

PUT /something/run

And an intermediate server gets its signals crossed, relies on the idempotent nature of PUT and retries the request over and over:

PUT /something/run
erk
PUT /something/run
erk
PUT /something/run
erk
PUT /something/run

…then I’ve just told my service to run a whole marathon without intending.

So I have to use POST when I invoke a verb, so that the in-between infrastructure knows to just do it once, and not to repeat the command if no response comes back.

POST /something/run
erk
(stop)

I think that’s a pretty marginal loss, though. The worst case is, it moves the retry logic all the way back into my client. But maybe that’s not even a bad thing.

OK so verbs kill PUT. Waaaa! But what else do we lose?

Well, we lose some benefits from being able to route requests through intermediate servers. So, for example, if I want to use a load balancer to partition my data, I can do that easily with a URI like this:

PUT /service/customer/213223423

I don’t have to examine the body of that request, my routers can inspect the URL and direct the request to the right bank of servers to handle customer 213223423.

But I think we can still get that with verbs. Let’s imagine that I have a method in my customer service service that increments some counter for a customer. The call looks like this:

POST /service/customer

{ action: increment,
id:213223423}

Hrm. But that’s awkward, because the intention of my call is buried in the body, which makes it hard to get to until very late in processing the request. So let’s make it better.

POST /service/customer/increment

{id:213223423}

OK that’s better, because now if I wanted, I could put a rule in my router and direct all the increment actions to a dedicated bank of servers. But wait, I can do even better than that.

POST /service/customer/increment/213223423

{ }

Now I can route this one based not just on my intent, but on the id of the target customer. Now I get all the visibility of my intention and the intended target all in the URL where I can look at it and work with it at a very high level.

There’s an additional benefit. If a REST purist makes a fuss about the verb in the URI, you can add two characters, and you’re back in happy REST land:

POST /service/customer/incrementor/213223423

{ }

I changed the verb to a processor noun, and WALA! turned the URI into the address of a dedicated processor object that will handle incrementing that customer’s counter for me. Happy REST purist!

So I’m thinking that verbs in URIs look pretty OK, especially if you include the identifier in the URI as well — it locks that invocation of the verb to a particular resource.

Too bad about PUT, though. But there are still lots of nounds we can still use PUT on and take advantage of its idempotent nature.

I should admit this is not pure speculation; I already rolled out an API that was a wrapper for a client built around the Command pattern. The API used Java reflection to map URIs to individual Command class instances:

http://server:32323/command/Login/byUsername

…and that would instantiate a Login.java command object using the “ByUsername” constructor (as tagged with an annotation). Parameters were just URL-encoded. We talked about allowing JSON-encoded parameter lists, but were too lazy to actually implement that.

If I had to do it over today, I’d do the exact same thing, except I’d include the identifier in the URI as well. So a LookupAddress command object, using the “billing” constructor, for customer # 213223423 would look like

http://server:32323/command/LookupAddress/billing/213223423

That crams a lot of critical information right in the URI where it’s easy to get to. If there are additional paramters, they could go in the query string or body of the request.

That API, by the way, was a HUGE success. Two years later, I’m told that business groups are picking up the API and running with it, without even telling the team that they’re doing so. We generate documentation on the API — Commands, constructors, and parameters — using introspection on the methods and their APIs, so it’s a very broad API with a couple hundred methods, but they’re all laid out in an easy-to-reference generated web page. It was so successful in one project, that our collaboration was reported all the way up to the CEO of our huge, multinational corporation.

So, ya, embedding verbs in URIs might not please the purists, but it can make for a highly accessible, practical API.

Advertisements

2 Comments

Filed under opinionizing, REST

2 responses to “A proposal for REST and Verbs

  1. Pingback: Scott Banwart's Blog » Blog Archive » Distributed Weekly 88

  2. Pingback: This Week in #REST – Volume 33 (Jan 29 2011 – Feb 8 2011) « This week in REST

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s