How do you design a good REST API with the use of HATEOAS, while retaining a thin client?
To me, it seems that using HATEOAS would break the possibility to use a thin and rather stupid client to access a REST API. Is it possible to design a REST API that uses HATEOAS, while retaining the thin client paradigm and if so, how?
Posted Feb 3, 2015, 9:31 AM UTC
67
What is a thin client to you? And how does it differ from a stupid client?
I would consider a client thin and stupid when it does not need to know anything about the implementation, nor would the client need to handle cases depending on circumstances. Let me explain with an example. Let's look at the HATEOAS principles as described on Wikipedia, not that fancy I know, but it illustrates my point quite well.
If I would like to implement a client for such a service, I would need to be able to handle that the service in question could return very different responses, depending on a multitude of factors. This example is of course simplified and doesn't seem that complex, but a real world example might have multiple factors in play, which could lead to a highly complex client.
The opposite would be not to use HATEOAS and let the service return a null result if there were insufficient funds to do a withdrawal. In this case, the client doesn't really need to handle anything, it just gets a null result and can push that on to the application it works for.
#####
######
#### HATEOAS:
^Interesting: ^Link ^relation ^| ^Stateless ^protocol ^| ^Federal ^Register ^| ^Java ^API ^for ^RESTful ^Web ^Services
^Parent ^commenter ^can [^toggle ^NSFW](/message/compose?to=autowikibot&subject=AutoWikibot NSFW toggle&message=%2Btoggle-nsfw+cobz9tj) ^or [^delete](/message/compose?to=autowikibot&subject=AutoWikibot Deletion&message=%2Bdelete+cobz9tj)^. ^Will ^also ^delete ^on ^comment ^score ^of ^-1 ^or ^less. ^| ^(FAQs) ^| ^Mods ^| ^Magic ^Words
It is a common misconception that clients that do connect to HATEOAS enabled services should be dumb or thin in a sense that they behave like a browser. Well the browser is a example often pulled on what HATEOAS means. But HTML, CSS and JS are complex models and the browser is a complex client that runs these complex media types. In a sense, the browser is dumb, because it just displays a web page (as a state machine). But the process of it is really complex compared to what the usual REST client should do for machine to machine interaction or for providing a custom UI to a human.
HATEOAS is not about the complexity of the client. It is about the media type itself and how it shall be the interface for the client. You can build a complex client for a really simple REST service or a simple one for a complex service. When you build a REST service around the HATEOAS concept, HTTP becomes just a transport and could be even exchanged for any other protocol that can transport a payload. For instance, E-Mail can also an example for HATEOAS. That treatment is the essence of HATEOAS and enables the service owner to change things on the transport level (e.g. changing resources due to refactoring).
The Wikipedia article can be misunderstood. For instance, it states:
The important part is the second part of the scentence: "or server beyond a generic understanding of hypermedia". So that means that a client should not need prior knowledge of how to interact with the server. But it does need to know about the hypermedia, the server talks. And this hypermedia may be really simple or the server may talk different media types (for different resources for instance, like "deposit" or "withdraw"). Or the media types can be really complex like HTML or CSS.
Your "factors" of the server response, should be documented in a way that you can build a client with it. The media type itself can be a very strict DSL (domain specific language) for a client. Hence, it could be just code that you can execute in your client (hello Javascript). There is the freedom. That is why a REST service should use HATEOAS so it is not directly driven by the transport (HTTP) anymore.
On the Wikipedia example: The example shows just one decision out of many possibilities on how a media type can look like based on the account balance. There is no one claiming that this is the only solution. Maybe a better approach would be to keep the links ("withdraw", "transfer", ...) on a negative balance, but add an attribute that states that those resources would return a denial if called under the current condition. Or if the links are kept entirely, the resources behind the links could state (when being followed), that a withdraw or transfer is not possible due to the balance. So, the decisions on how the media type looks like and behaves is entirely up to the functional design of the REST API.
And your client can be as dumb as the media type documentation suggests.
EDIT:
With other words: A client that talks to a REST service "A" is not obligated to understand a totally different REST service "B", even if A and B provide their APIs under the HATEOAS principle. But that is what people often think when they hear about it.
Thanks, that really sums it up pretty well. From what you're saying, as long as the client actually implements all of the funcionality of the API, it doesn't really matter if you use the HATEOAS principle to create a discovery like API or just expose the full API through documentation.
Your client does not need to implement it all. For example, if you write a client against an API of a bank to show stock information, you would not implement the stuff that's about banking. The discovery should be driven by the media types, though. Take this example:
First request:
First response:
Second request:
Second response:
Yes, your client would need to have an extra request to do in order to get the "stock-information" resource. But your client can cache it (depending on cache headers sent by the endpoint). And if the endpoint changes somehow while you have payloads cached, you would restart as soon a request is responded with an error. This restart could be transparent to your users. The client would just show a loading screen a bit longer.
Also note, that I used different media types for different resources. The documentation would be about those media types. It would explain the links, the meaning of the relations, what media types are served behind the links and of course the other functional features. The neat thing is, that the bank could now change things without breaking your client. For example, lets say the bank is transitioning to a new version in iterations. And they took the "stock-information" first. Your two communications would look like this:
First request:
First response:
Second request:
Second response:
So using HATEOAS, the bank decoupled their implementation details from business logic.