programming is terriblelessons learned from a life wasted

How I explained ReST to a Troll

As a card carrying member of the church of http and all that is Fielding, I’m often prone to pedantical fits when I hear people talking about ReST. It’s up there with agile, as a technical term which has lost it’s meaning as it has been adopted or co-opted by the community at large.

RPC goes back a long way, and one of the earliest, RFC 707, describes it as a way of sharing resources over a network, but the modern usage is probably closer to remote function or method calls. So let’s talk about remote method invocation over HTTP, usually with the following components–

And if you’re lucky, a schema for the latter two which can be hopefully generated into stub client code. JSON-RPC, XML-RPC and SOAP all fall into this pattern. Tunneled over HTTP, but. not taking advantage of HTTP Let’s see if we can make RPC play nicely with HTTP, and get web-services to behave more like websites.


Step 1: Different things have different URLs.

Instead of having one endpoint, let’s expose the class and method name in the URL. Instead of /endpoint, we’ll go to /endpoint/Class/method. Now we can get http servers to route methods to different machines, transparently to the client.

Step 2: Not everything needs to be POST

Although POST works well, some requests don’t have side effects, and can be safely retried. Let’s take those commands and use GET instead. Now middleware can retry commands if they fail, without having to inspect the messages.

Step 3: Caching is good for you.

Caching is a useful tool to stop websites falling over under load, and web-services should be able to take advantage of it. By adding cache-control headers, we can avoid hammering a service for relatively static information, or responses that don’t need to be accurate. Additionally, Edge caching makes this a tantalising proposition.

Step 4: Expose the data types.

We can start by exposing the schema at the end point, adding a header that says “This response uses this schema”. A great place to do this is in the content-type: field. Instead of just “application/json”, we can use “application/vnd.fartapp.fartcount+json”. Now clients can ask for the version they understand, rather than breaking the API and starting afresh. When you update a schema, both versions can be served concurrently, without breaking old clients.

Step 5: Expose the service

You can open a website in a browser and click around to find pages, without having to remember all the URLs in advance. Being able to explore the service, without having the schema in advance means it’s far easier to debug and check what is going on.

If you connect to a web-service endpoint, it can list the classes and methods available. If you get a response, it could also tell you what methods are available too, and you can even add new methods into the list. Old clients can ignore them, but new clients can check the responses and use the new options available. This loose coupling allows for a smooth migration path.

Step 6: Make the URLs Opaque

Instead of the client knowing where everything is advance, we can tell the client in the responses where to go next. Websites already take advantage of this, Images are often served under a different domain, and URL schemes can change but the interface stays the same. When a service grows too large, it often gets split into different clusters or services. We can break the existing API and get the application to track this, or we can put a router in front of the services which knows where to send requests.

We can even run clone services on different domains entirely. The only URL that changes in the client, is where it starts, and the rest it learns as it goes along, navigating responses.

Step 7: Carry state inside the URLs

Clients now open resources, follow links, and submit requests, all without knowing the exact URL schema. This means we can hide things within the URLs, to carry information from one request to the next.

When you go to a forum, click on a thread, read a post and hit reply, you don’t have to repeat yourself and tell the server which thread or post you’re replying it. The link you clicked has this information inside “/forum/reply?id=12345”. Web services can do a similar trick.

A common tactic to scale out services is to fragment them, or shard them. Hosting different parts of the data on different services. With a traditional RPC API, you have two options, either add this state to every method exposed, or put middleware infront to route the requests. With opaque URLs, you have a third option.

Your endpoint can redirect requests to /endpoint?cluster=A, and expose the same information, but the URLs in the response all have that same bit of state inside. Instead of breaking clients, or middleware, you can let the client handle it transparently.


So is this ReST? Maybe, but really it’s loose coupling between services, and exploiting HTTP for all that it is worth. By taking these steps we’ve turned a hardcoded set of stubs into a loosely coupled system which can change and adapt without breaking compatibility, and we can debug it too. We’re not hardcoding locations or URLs, but hardcoding request and response types. We’re no longer running a web service, but running a machine-readable website.