Modified RPC over HTTP: old wine in a new bottle

2010-04-12 @ 17:31#

this post is a follow-up -- a prequel, actually -- to my earlier post entitled A RESTful Hypermedia API in Three Easy Steps. this time, i'll start from the same basic premise (a simple list manager service) and create, not a RESTful interface, but a more typical HTTP interface that is too often labled RESTful when it is really a modified RPC interface over HTTP. the aim here is to help reader distinguish between RESTful and RPC implementation for the same service.

you can learn more about my RPC, Modified RPC, and Hypermedia labels for APIs in another of my blog posts (Not all Web APIs are the same).

The List Service

i talked about this in the last post, so here's a real quick review. this API will define a simple list management service. you can use it to add, edit, and delete items from a list like a "to-do" list, a shopping list, etc. you can also execute a few simple queries against the list including all the open items, all the items due today, or items due within a date range.

if i was coding a local application, i might use a set of function signatures that look like this:

GetList()
GetItem(id)
AddItem(name, description, date-due, completed)
UpdateItem(id, name, description, date-due, completed)
DeleteItem(id)
GetOpenItems()
GetTodaysItems()
GetItemsByDate(date-start,date-stop)

for this example, you can assume the above set of methods are implemented on the server in some way. for instance, these might be implemented as a single class with a collection of methods. or the above calls might be implemented as a set of sprocs in T-SQL. the details of the server-side implementation is not really important, all we really need is an interface exposed to HTTP. that's next.

Modified RPC over HTTP

usually, i see folks taking the above set of method calls and converting them into a set of HTTP URIs along with a message body in one or more serialization formats. here's a typical example:

GET /list/
GET /list/{id}
POST /list/ 
  name={text}&description={text}&date-due={date}&completed={int}
PUT /list/{id} 
  name={text}&description={text}&date-due={date}&completed={int}
DELETE /list/{id}
GET /list/?today
GET /list/?open
GET /list/?date-start={date}&date-stop={date}

as you can see, this URI-centric interface is just another RPC approach that matches the URI and the HTTP protocol method (GET, PUT, POST, DELETE) to the existing class methods. it's not RESTful at all. not that there's anything wrong with that. .

The Downside of RPC over HTTP

often the modified RPC approach leverages well-known data formats such as XML and JSON. these types are devoid of built-in semantic link information (or hypermedia). more recently the Atom media-type (an XML format with CRUD-style semantic links) has been used as a payload for modified RPC designs. the Atom Publishing Protocol (RFC5023) provides basic navigational and update link semantics for the Atom media-type by defining two URI types (Collection URI and Member URI) which can be used by clients to handle standard read and write operations. this is an improvement over JSON and plain old XML (POX) designs.

however, even when HTTP APIs use the Atom hypermedia format, most API designers fall into the pattern of documenting a wide range of URI conventions for a single protocol (HTTP) that include detailed rules for making queries against the data store. clients must be provided with custom coding in order to support these query rules; rules which are rarely transferable between implementations or applications. also, most Atom-formatted content carries the interesting data as a serialized custom XML sub-document within Atom's <content /> element. This approach treats the Atom format itself as an envelope for custom XML much the way SOAP is designed to be an envelope for RPC operations. What follows is a typical example of a popular Atom representation that uses the <content /> element to carry a custom XML serialization (the <m:properties /> element in the example below).

<entry>
  ...
  <content type="application/xml">
    <m:properties>
      <d:CustomerID>ALFKI</d:CustomerID> 
      <d:CompanyName>Alfreds Futterkiste</d:CompanyName> 
      <d:ContactName>Maria Anders</d:ContactName> 
      <d:ContactTitle>Sales Representative</d:ContactTitle> 
      <d:Address>Obere Str. 57</d:Address> 
      <d:City>Berlin</d:City> 
      <d:Region m:null="true" /> 
      <d:PostalCode>12209</d:PostalCode> 
      <d:Country>Germany</d:Country> 
      <d:Phone>030-0074321</d:Phone> 
      <d:Fax>030-0076545</d:Fax> 
    </m:properties>
  </content>
</entry>
  

The Bottom Line

in general, URI-centric designs like the modified RPC example discussed here tend to ignore the value of semantic links and subsequently require clients to know the exact URIs and/or detailed URI construction rules before they are able to interact with the server. this can lead to tight coupling between the server's documented URI scheme and any deployed clients. changes in the URI scheme run the risk of breaking existing clients or at least rendering some features unavailable to them until the clients are rebuilt with the new URI construction rules.

NOTE

this limitation on client-coding doesn't come up when the server developer is also the one charged w/ coding the client. however, once third-party developers start building their own clients against this RPC API, things can go badly. More than one social service API that relies heavily on third-party clients has, after making seemingly minor changes in their RPC-based URI conventions, incurred the wrath of jilted developers.

finally, when surfacing the function signatures of classic RPC designs to the protocol level via HTTP methods and URIs, the design becomes protocol-specific. in other words, any change in procotol (e.g. FTP or SMTP, etc.) will resullt in additional URI construction rules. this further limits a server's ability to modify the interface in the future.

So...

in the long run, it's better to implement RESTful interfaces using hypermedia links in a shared, well-documented media-type as in the example given in the post metioned at the beginning of this entry. i favor minting new media types that support one or more application implementations. however, it's also possible to use existing media types as long as you provide additional documentation that developers can use when coding clients and servers to use your hypermedia interface.

maybe i'll implement a hypermedia inteface using a standard media-type soon and post it here, too.

REST