Skip to content
 

Opinionated (RPC) APIs vs RESTful APIs

If you are not already aware, a few years ago there was some debate on the Internet as to when it was ok to call an API “RESTful”.  I won’t go into all the details, but suffice it to say that the well-respected originator of the term REST did not agree with the way his ideas were being implemented.  One outcome of this debate was that we (the loose community of API developers) now use the ugly acronym HATEOAS to refer to the style of REST which its creator envisioned, and the purists don’t freak out too much when people call their “other” APIs RESTful.

As of 2012, the scale of HTTP web services looks something like this (overlapping circles indicate shared principles):

Scale of web service architectural styles

My own API product (node-perfectapi) is biased towards the RPC side, with many of the advantageous RESTful principles built-in.  It is that way because I am a proponent of opinionated APIs, and I don’t think that the document/resource architectural style is a good fit for that.  In fact, I don’t think it is a good fit for any scenario where your data is not naturally a document.

I had a sneaking suspicion I might be missing some greater truth though – after all, the constraints on the right hand side must have associated benefits, right?  So I started investigating…

The Benefits of REST

One of the perceived benefits of doing REST is that the many intermediate layers in the Internet can handle caching.  I looked at the popular caching product Varnish, and was surprised that it is unable to automatically take advantage of even the most basic REST principles.  Out of the box, it caches the GETs that you explicitly tell it to.  Cache invalidations are configured manually.  In my research, other caching products seem similar.   The bottom line is that as long as you use idempotent GETs for querying data, you are doing just fine.  Beyond that, there is no inherent caching advantage to be gained by doing REST.

Some other benefits are more of a slam-dunk.  Content negotiation (return JSON or XML based on the request headers)  is a nice way to ensure that clients can talk in the language that is most natural for them.  Stateless servers are a well-proven boon to scalability.  But… these same principles are easy to incorporate into RPC too.

The one RESTful thing that is unnatural for RPC is the document/resource oriented paradigm of “one URI for each resource”.  There is a benefit of consistency, in that the same resource format to POST/PUT a resource is what you GET back when query the resource.  It promotes a nice warm feeling in my tummy when things are so nice and symmetrical.  Documentation is simpler because you spend time documenting the format of the resource once, instead of documenting several RPC functions, each of which may involve some or all of the same resource.  Community acceptance is also better, because REST is currently in favor.

The Downsides of REST

There are several downsides to RESTful services, like

  • figuring out PUT vs POST (for both client and server developers),
  • making use of PATCH, and generally dealing with partial documents
  • dealing with gateways and proxies that don’t support arbitrary HTTP methods
  • running out of HTTP methods on an endpoint
  • you still have to read the docs to understand when to use which HTTP methods.  Its not self-documenting (but it is more self-documenting than RPC!)

An Experiment – converting RPC to RESTful

As an experiment, I decided to try to re-design a simple RPC API in a RESTful way.  I chose node-sharedmem, which is a Node.js HTTP server that can be used as a shared memory space for processes that have that need.

The functions on the RPC-based API are as follows:

  • save(collection, key, value, [TTL]) – saves a key-value pair in a named collection, optionally set to expire in TTL milliseconds
  • get(collection, key) – retrieves a saved value from a collection (returns the value)
  • remove(collection, key) – removes a saved key-value pair from a collection
  • increment(counter) - increments a named counter and returns the new value (integer)
  • decrement(counter) – decrements a named counter and returns the new value (integer)
  • getArray(collection) – returns all key-value pairs within a named collection

This is a very opinionated API, with no concept of a document.  I can easily identify some resources though – collections, counters and variables (key-value pairs).

I designed the following REST URIs to replace the functions, and return the same results:

  • /collection/{collection}/variable/{key} – POST, same as save function
  • /collection/{collection}/variable/{key} – GET, same as get function
  • /collection/{collection}/variable/{key} – DELETE, same as remove function
  • /counter/{counter}/increment – POST, same as increment function
  • /counter/{counter}/decrement- POST, same as decrement function
  • /collection/{collection}/variables – GET, same as getArray function

(For the counter, I decided to hard-code ‘increment’ and ‘decrement’ in the URI, rather than the more risky approach of inventing new HTTP methods).

Somewhat surprisingly, this was easy to do and the result looks RESTful to me!  (But it is not HATEOAS – meets none of the unique characteristics shown in the blue circle on the diagram).

If I had to critique the solution, I would say that

  1. it exposes too few endpoints (a more RESTful solution might expose endpoints to show all the counters, or all the collections).  This limits discoverability.
  2. the GET of a variable does not return the whole resource – it excludes the TTL and just returns the value.  So we don’t have the resource-format consistency gain that we expect from REST.

Conclusions

It is easy to put a RESTful face on an RPC web service, but the facade will not bring the full benefits of consistency (of a common resource format) and discoverability (of a complete set of resource endpoints).

That said, you do get some of the RESTful benefits – consistent endpoints, and discoverability of the current functionality.  In addition it is way less work than designing a full, discoverable RESTful API, because the full API has many more endpoints than you might want to create, test and support.

Afterthoughts on HATEOAS

I think there is very little there that is of use for APIs.  It works well for web pages, but APIs require more shared knowledge (coupling) than shared knowledge of media types can provide.

The main HATEOAS tool that many people have started to include in their APIs is the link-relations.  It is useful to have these in several scenarios, e.g.

  • paging – returning a link to the next page of results makes it much easier for both the client and the API developer
  • linking to related data – for example, a document might have links to more detail, or history, etc.

Even those links are not really HATEOAS though –  to be HATEOAS, the knowledge of how to interpret the links has to be derived from the media type of the document.  For example in HTML we know how to interpret HREFs and FORMs.  In the common formats of JSON or XML we have no such standard.

 

2 Comments

  1. A problem with both the “opinionated” version and the “REST” version of your API appears when you consider partial failures. Assume that you made an increment or decrement call and it failed. What are you going to do? Are you going to repeat the call? If the network connection failed after the counter was decremented but before the response was delivered back to you, you are going to increment ti twice. If you don’t repeat the call, how are you going to ensure that your request was executed?

    You can avoid this problem both in the “opinionated” version of the API and the “REST” version of API. The key is to avoid non-idempotent operations like increment or decrement. Most “REST” designers will immediately notice that you are using POST and POST is not guaranteed to be idempotent. Most experienced RPC designers will also notice the non-idempotent method in the “opinionated” version. However, if you’d ask me, I would give the REST designer a better chance of noticing it.

    Does this mean that “REST” is superior? Not at all. But it means that when you are offering your API over a relatively unreliable Internet to a large number of developers of varying skills, REST might give you an advantage.

    This comment comes not from a REST fanatic, but from someone who worked with DCE RPC, DCOM, CORBA,and SOAP most of his career.

  2. [...] Opinionated (RPC) APIs vs RESTful APIs (03.2012) 0 comentarii » [...]