Not Hating on HATEOAS

There's been some loving and some hating on HATEOAS (which I don't know how to pronounce), but I'm starting to get it. See: REST Cookbook, Timeless, and PayPal's API.

The core idea is, in addition to the data, you send over some information about the possible URLs you can use as a next step.

So, if you return a list of objects, each object contains a complete URL to retrieve the details of the object. If the list is a page within a larger result set, you also send 'next' and 'prev' links.

If an object can be altered, you send links for each action you can take upon the object (with the object ID embedded).

While some are saying a HATEOAS response contains everything you need, and others are saying HATEOAS is failing to deliver on that promise, I don't think they're getting it. What you get is information about what is possible within the narrow context of a of a single object or list of objects. A client application still needs to present these options to the user.

What HATEOAS does is relieve the client of needing to know (as much) about the logic within the server. I haven't written any HATEOAS code yet, but it seems obvious where it will help.

Normally, in any client, you need to make decisions about what UI elements to activate, or information to reveal, based on the application state and the permissions the current user has to view or alter some resource. You end up with code like this:

  if (user.canDo(something, thingX)) {
      ui.enable(thingX);
  } else {
      ui.disable(thingX);
  }

And you also end up putting the logic in the client.

  function canDo(action, object) {
     perms = lookupObjectPerms(object);
     return calculatePermissions( self, perms );
  }

It's not much (pseudo)code, but it requires a lookup, then a calculation. So we need some permission info either stored on the client, or delivered from the server... or worse, calculated at the server each time you call calculatePermissions().

Then, there's the server side. You need similar code on the server side - and the logic on both client and server must match! That's terrible.

Pretend that I've duplicated the pseudocode above, here.

It's not like you can avoid writing the server-side logic -- if you screw it up, you might create security holes, or access problems.

A HATEOAS-compliant API allows you to keep most of the logic encapsulated within the server side code; the client side code loses the business logic and only has UI logic. The UI logic can be based on whether a specific URL was returned from the server. If the URL is not present, you disable that UI element.

It doesn't relieve the client-side programmer of knowing the semantics of the URLs, or of the data. It doesn't relieve her of having an overall application logic -- after all, part of the appeal of REST is that you have a bunch of available services, and the client presents a largely non-modal environment to the user, and the client controls the user experience. HATEOAS doesn't enable any kind of "magical universal client."

What it does is allow the server programmer to communicate a subset of valid actions that the client can perform, within the context of the specific request for a specific object, or a specific list of objects. The calculations are done the normal way - via user ids, object ACLs, and other permissions.

It's harder to write this code than the traditional kind, where you allow all possible methods, and then throw errors on invalid methods. You can't just say "there's a get(id) method you can use" and then when they do a "get(1234)" you throw an error.

Instead, you have to look what was returned, and look at the permissions, and then determine what valid IDs can be get()ted, and then generate URLs for each, and send those links to the client.

The only headache I see is that you write logic to check the permissions once to prodce the links, and then check again after the request is made, to determine if it was a valid request.

The upshot: you reduce duplicated code; you reduce the total number of lines of code; and you encapsulate critical logic on the server, relieving the client programmers from having to synchronize their logic with the server's logic.

References

Do you hate HATEOAS
PayPal API