Better Hypermedia Through Obfuscation
Posted by Dylan Beattie on 23 February 2016 • permalink Here’s a fun and completely twisted idea that came to me on the train this morning. One of the constraints of ReSTful architectures is that they use hypermedia as the engine of application state – in other words, clients shouldn’t maintain their own information about where they “are” and what resources and actions are available to them right now; they should rely on hypermedia embedded in the current resource representation they’ve retrieved from the server. A common example of this is pagination – navigating big lists of resources, using a representation something like this example:Now, with an API like this, it’s all too easy for the client – or rather, the person building the client – to go “ok, page number is a zero-based integer; let’s cut a few corners here” and just program something likeGET /people "_embedded": { "people": [ /* array of 10 people */ ] }, "_links": { "first": { "href": "/people?page=0" }, "last": { "href": "/people?page=12" }, "next": { "href": "/people?page=1" }, "self": { "href": "/people?page=0" } }, "count": 10, "total": 115 }
Now, I’m a big fan of something we call the "pit of success" – the idea being that we "build platforms such that […] developers just fall into doing the 'right thing'", and more generally, the idea that the easiest way to achieve something is also the ‘correct’ way to achieve that thing. So what if we intentionally obfuscate our APIs so that hypermedia navigation becomes easier than building a for() loop? By, for example, requiring that our page number is written out longform, instead of numerically? And, just for fun, we’ll require that it’s in a language that isn’t in the regular ASCII character set. Like Ukrainian:for(var i = 0; i < 12; i++) { http.get(“https://api.foo.com/people?page=”+page); }
Suddenly, your ‘shortcut’ isn’t a short cut any more. For starters, you’ll probably need to install a special keyboard in order to type those characters – not to mention suddenly your source code files will need to be in UTF8, and I’ll wager that somewhere in your build pipeline there’s a tool that can’t handle UTF8 source code. And you’ll need a routine which can translate integers into Ukrainian string representations… No, the easiest thing to do now is to retrieve the first resource, parse the next.href property, retrieve that resource, and so on until you hit a resource with no next link. Which, of course, is exactly how hypermedia is supposed to work in the first place.{ "first": { "href": "/people?page=один" }, "last": { "href": "/people?page=дванадцять" }, "prev": { "href": "/people?page=два" }, "next": { "href": "/people?page=чотири" }, "self": { "href": "/people?page=три" } }