8 ms·
It's just never been clear to me what HATEOAS is really supposed to be good for. Sure, a client can follow the links in an automated fashion, but how is it supp
by zerocrates 10y ago
It's just never been clear to me what HATEOAS is really supposed to be good for. Sure, a client can follow the links in an automated fashion, but how is it supposed to know what the resources actually are and which links it needs to follow, which resources it has to create or modify, to actually accomplish anything?
The general idea of returning links to related resources and/or actions is fine and good, but the rhetoric tends to go further, to ecompass claims like the API being "self-documenting" or amenable to a universal client. It always seems to me that this Big Idea of just presupposes the existence of a "smart" client that can really understand the links, one that there doesn't seem to be much sign of.
GitHub's API proudly notes its use of hypermedia and URL Templates in its responses, but I still have go read the documentation to decide what link I need to use, and what needs to fill into those variable slots in the URLs. The template doesn't do much for me that text in the documentation saying "these GET paramters are accepted/required" wouldn't just as well.
- deleted 10y ago[deleted]
- squeaky-clean 10y ago> Sure, a client can follow the links in an automated fashion, but how is it supposed to know what the resources actually are and which links it needs to follow, which resources it has to create or modify, to actually accomplish anything? I've never understood this either. My API client isn't smart enough to follow links and write logic for me, so when they say "the client" can "discover", they must be referring to myself, and not my code? Well I'd much rather read documentation than click hyperlinks inside an API.
- webmaven 10y agoYou can find smarter clients by Googling "hypermedia client". Also, O'Reilly just published a book on the topic: http://shop.oreilly.com/product/0636920037958.do http://shop.oreilly.com/product/0636920037958.do
- RyanZAG 10y agoBut would anybody really want to use a hypermedia client versus a regular client? Of course not. The regular client can be optimized properly at the level it needs to be to make the user's interaction smoother.
- webmaven 10y agoYou are entering your comment into a general purpose hypermedia client for humans, why wouldn't you want a general purpose client for machines?
- jalfresi 10y agoThis is because the idea is that you would create a new media-type to represent your resource. It is this media-type definition that would determine what rel-types there are and how a client should interpret them. for example, the spec for the HTML media-type that when a client sees a link with the rel-type "stylesheet" is should fetch the resource using HTTP GET. As REST requires that media-types be registered the idea would be that we would eventually get a set of media-types that cover things like audio playlists, and how to interact with them. So any "intelligence" required by a client would be baked into the implementation of the media-type processor. Instead of "client libs" for specific web services, you would have a general media-type parser/processor which could be re-used by clients of different web services to process common media-types. But apparently individual client libs for each web service that overloads JSON is better.
- tokenizerrr 10y ago> But apparently individual client libs for each web service that overloads JSON is better. I mean, there are so many different types of resources and every API I interact with definitely invent their own. How often do you come across an API offering a playlist of music?
- jrochkind1 10y agoYep. In theory, one could imagine various schemas at various levels of abstractions that allow the client to 'know' what's going on, automatically deriving it from the schemas. I think this is also the theoretical promise of one approach to linked data/RDF. In practice, I don't think it happens, and is not worth the conceptual and engineering overhead for the possibility of something that doesn't seem to be realistic to expect.
- aldarn 10y agoIndeed you need a client that is capable of mapping allowed methods and links to something useful, but it's not hard to imagine how one might go about this: * For related resources (imagine a DB FK), instead of listing a uuid you point a link to the location of that resource. This means if the resource location changes the client doesn't need to be updated, it just uses whatever the new link is. * Methods listed can drive available actions, e.g. imagine you have both mutable and immutable objects, a smart client could reason if PATCH is available then a UI element can be spawned allowing the user to modify this resource. This is useful in a single client scenario whereby the API schema can be modified to a certain extent without the need to modify the client. It is also useful in a multi-client scenario (either distinct or versioned clients) for ensuring consistency between clients as you no longer need to ensure all clients are modified to use the new schema. At least, this is how I understood it, I haven't tried it in practice.
- hadriendavid 10y agoHow I interpret HATEOAS: The client knows what and how it can access on the behalf of the authenticated user. Examples: Representation for a user with no privileges: { "articles": [{ "id": 123 "title": "A title", "links": { "self": { "href": "http://blog.com/articles/123", "methods": ["GET"] } } }], "links": { "self": { "href": "http://blog.com/articles", "methods": ["GET"] } } } Representation for a user who is authorized to add/edit/delete articles: { "articles": [{ "id": 123 "title": "A title", "links": { "self": { "href": "http://blog.com/articles/123", "methods": ["GET", "DELETE", "PUT"] } } }], "links": { "self": { "href": "http://blog.com/articles", "methods": ["GET", "POST"] } } } This reduces the authorization logic on the client side.
- ryeguy 10y agoHow? At the end of the day you're still writing an "if" statement branching on a piece of data in the response. In a regular api it might be canEdit, here it's the http verb in an array.
- fosk 10y agoTo me the best advantage is that by following links, the client doesn't have to builds those links in the first place. So the client will keep working even if a few months from now you want to change the link to something else. A simple example: if retrieving articles can be done by requesting this link "/articles", I can potentially change it in the future to "/v2/articles" and the client would still work.
- funkymike 10y agoThe client may not have to build the links but it still has to be aware of their specifics. It has to know that /articles takes arguments X and Y, and that /v2/articles takes arguments Y and Z. You're trading building links to inspecting links. What advantages does this have?
- dmsnell 10y agoOne of the notions in HATEOAS as far as I can surmise is that we decide to build a common vocabulary describing our API and that becomes the fixed interface for interaction. It's not at all meant to imply that software clients can magically figure out what to do next (thought often a human with a reasonable API app could do so). Imagine for example that we decided that whenever you request a comment resource from my API then I promise to provide a link in the response called "upvote" which you can follow to upvote that comment. We have agreed to a fixed interface but left plenty of details flexible. Maybe we are experiencing heavy load: let's stop sending the "upvote" link - the clients should understand that in its absence the operation is not currently available. Maybe we have implemented some load-balancing system which redirects clients to `fiji.api-server.com` or `romania.api-server.com` based on their geo-ip data: those clients need only hard-code one top-level API URL into their code and all other URLs come through successive API responses. Load-balancing happens automatically and can even change throughout a single session because the client follows the links instead of building its own URL. Maybe we are running some test or gradual rollout of a new API or URL structure; as long as we provide those links and references the clients can follow the right path without needing to know about the changes. It's possible that we ended up moving comments from `api-server.com/api/threads/1337/comments/42` to `api-server.com/comments/what-do-you-kow-joe` and this won't break any client designed to follow the interface instead of the incidental details. For what it's worth I think very few APIs come reasonably close to this design and maybe few even have much need to. REST and HATEOAS become much more important when someone is publishing a public API that many third parties will consume and the ability to introduce non-breaking changes and server-side control of different specifics is important. /my 2¢
- dragonwriter 10y ago> It's just never been clear to me what HATEOAS is really supposed to be good for. Have you ever used a web browser? You know how the browser uses media-type to determine how to handle content referenced by a URI? That's what HATEOAS is supposed to be useful for: links identify and locate resources, resource type information tells you what kind of resource it is. The only out-of-band information you should need for an idealized REST API is information on the protocol used (e.g., HTTP) and information on the resource types (media types for HTTP) of the resources used. (Really, if you want to understand any component of REST, its probably easiest to ask "what is this used to accomplish in the HTTP-based web", since REST is essentially a generalization of an idealized version of the HTTP-based web.)
- paulddraper 10y agoYes, but most people aren't making web browsers or similar. Web browsers are extremely generalized: they display arbitrary HTML, submit arbitrary forms, download arbitrary images, download and run arbitrary JS/CSS. Most APIs are intended for more specific uses than "all of HTML". Hypermedia is very useful for browsers, but I wouldn't extend that to say, my Imgur clone API.
- dragonwriter 10y ago> Yes, but most people aren't making web browsers or similar. The question is whether people are making APIs to be consumed by general purpose tools, not whether they are making general purpose tools. But, yes, there's a quite valid argument that REST isn't always the right architectural style for an API. Which isn't an excuse to use the term "REST" for things that aren't REST, which just confused the issue.
- paulddraper 10y agoOkay, either way. There aren't a lot of hypermedia-based clients.
- rdtsc 10y agoJust guessing, if you had an API for creating documents for example, and you POST a request to /docs/ you'd get back not an just a single ID but a URL to /docs/<ID>. So then the client can operate on that resource and not have to compose it. It can also be browse-able with a regular browser. If you visit it say with Firefox and go to .../api/ and the browser tells the backend it accepts text/html back, the service would return a list of sub-resources as proper hyperlinks. Pretty formatted json and so on. Might return a few items only not all if there are too many in a collection. After you see all sub-resources .../api/docs/ .../api/widgets/ etc. you navigate by clicking to any of those.. I have actually built that and it seemed like gimmick first but improved developer productivity quite a bit because the API is discoverable and provides live data (instead of just out-dated examples from docs that nobody updates. Was it HATEOAS? I still don't know. But I knew it worked really nicely. Someone working on the backend could finish a feature and stick it in /api/newfeature/ a front-end developer would browser that get a sense of how it works, what data looks like and so on. (Bonus points: figured a way to auto-generate docs from code comments from classes and modules and pushed them to the API resources, so now it is was self documented, and had live example and so on).
- zerd 10y agoIf you get back an URL to the document instead of the ID, then whenever you need to refer to that document, you need the whole URL. That means that it can't change, which I thought was one of the arguments for using HATEOAS, that you don't need to hardcode the URLs, and can "evolve" the API without breaking clients.
- rdtsc 10y agoGood point. I think that might be a good thing too in the sense that you could support multiple API versions: by maintaining compatibility for older URL paths. Then clients could also crawl the hierarchy at startup or so to see if what they expect to be there is there or it changed. Maybe respond with a redirect if an older resource is accessed... In practice usually a v1 or v2 is shoved somewhere in the URL or headers.
- theptip 10y ago
- _pmf_ 10y ago(Note: I'm not advocating this approach, I merely repeat what I think I know) Instead of hard coding the subject hierarchy in templates (like "/topic/subtopic/"), you define a document type that exposes target elements as links (typically with a "rel" attribute identifying the type of element). The client can then navigate the logical hierarchy without requiring this to match the physical URLs (think: federation across departments; instead of having a template "/api/{department_id}/people/{employee_id}", the company can expose a directory document that aggregates links "link rel='employee' target='https://departmentX.company.com/arbitrary/hierarchy/employees/12345'" https://departmentX.company.com/arbitrary/hierarchy/employee... and "link rel='employee' target='https://othercompany.com/api/users/Doe+John'" https://othercompany.com/api/users/Doe+John'"). The type of linked resources must of course be aligned; there's no magic involved. Instead of the client having out-of-band knowledge about the hierarchy, the client must have out-of-band knowledge about the used document types containing the links.
- notjack 10y agoHATEOS, and REST in general, is a lot more useful when there are middlemen involved. If I have some link relation type named "api.myservice.com/rels/access-controlled-by" and some content type for authentication policies, then I can build a proxy between my API and clients that looks for links of this relation to resources of this type and automatically implements authentication checks. Instead of writing code to check auth rules in my API, I link to a resource that the auth proxy understands in a common format. This format can evolve over time without breaking the proxy due to content negotiation, and API services written in entirely different languages can still rely on a uniform implementation of authentication rules. There's all kinds of other directions you can take this including quota enforcement, monitoring, auditing, and other resource-agnostic concerns. More radically, you can make these sorts of proxies reusable services that other people rely on to implement these behaviors. One of the primary motivations for REST in the first place was a standard interface that would allow for insertion of caches at arbitrary points in the Web without breaking everything (in the optimistic case at least). There's even HATEOS in the Cache-Control header, as the cache channels extension uses links to external resources to define the cache channels for resources
- macca321 10y agoUse something like JSON-LD, where the payload contains hyperlinks to documents for each data property. Your client then can machine read that documentation (which can have long-lived cache headers or be immutable, so your client doesn't spend it's whole time re-checking documentation). There will be a set of definitions that the client understood when it was coded, and by checking a payload for matching meanings, it can consume the information. If it finds something it doesn't understand, it could even try to take some compensating action (e.g. suppose it's a client which displays images to it's users, and it encounters a new type of image format. It could perhaps look up a registry of javascript canvas image renderers and download suitable code to display the new image).