Hacker News new | past | comments | ask | show | jobs | submit login

I think this takes an overly-simplitisic view of APIs. Going by the primary example in the article, by representing a pet's owner as a link instead of an id, they're basically discounting the idea that there may be separate endpoints that take in an owner id. For example, if there was an endpoint that let you get the invoices by customer, you would still need to understand the templates for that endpoint.

More fundamentally, I think it's trying to solve a smaller problem in the face of a much bigger one, you still need to know what the response of any given endpoint is going to be. Just because they've passed me a link, doesn't me I don't need documentation on what endpoint that link points to. I still need to know that the owner is a link to the people endpoint so I can properly parse that result. That in turn requires just as much documentation (IMO) to describe the relationships as it would to properly document your URI templates.

Obviously, the primary reason to use links over ids is to give the developers of the API more control over changing things like routes and Ids and whatnot, but I feel like it is a bit disingenuous to make it out to be a much better user experience or something, since it really isn't.




I think the HATEOAS way to do this would be that you never calculate an endpoint like that. The owner object should have the invoices url for that owner available in the body of the owner object.

...Its ludicrously bloated and part of the reason why no one does "real rest."


> The owner object should have the invoices url for that owner available in the body of the owner object.

Actually, it doesn't need to. The owner object would only have invoice IDs. The invoice collection resource would be discoverable through the root endpoint through the same process used to discover the pet and owner collection resources.

In fact, the owner doesn't need to have any invoice information at all. The invoice collection endpoint can simply be queried to find an invoice related to the pet and/or owner.

> ...Its ludicrously bloated and part of the reason why no one does "real rest."

Actually it isn't, and the main reason why "no one does real rest" is because web API clients are required to perform content discovery, which is more complex than simply using hard-coded URLs/URL paths.

Of course this complexity is only apparent and superficial, as having to support a myriad of API versions ends up with a simple solution that due to all the technical debt that piles on rather quickly is far more complex and error-prone and unmaintainable than the approach required to do REST.


If I'm finding the invoice endpoint through the root instead of a link in the user, won't I need a uri template to know where to put the user id?


In REST you do not need URI templates. You discover the invoice collection endpoint through the root endpoint, and once you have a ID you can, say, query the invoice collection endpoint to get to the invoice.


Sure you're free to structure the exact responses how you like, but the point is that the urls are discoverable through api responses.

> web API clients are required to perform content discovery, which is more complex than simply using hard-coded URLs/URL paths

You mean using Hypermedia As The Engine Of Application State is cumbersome and why people don't do "real rest"? Yes. I agree.


> I think the HATEOAS way to do this would be that you never calculate an endpoint like that. The owner object should have the invoices url for that owner available in the body of the owner object.

HATEOAS is neutral as to resource representation; you could have the invoices collection and all the individual resources within the (or one of the; resources don't need one and only one representation) owner resource representation.

If the collection or invoices can be accessed as individual resources (which, again, HATEOAS is neutral about), they should have their own URLs, but that doesn't prevent them from being incorporated in the parent resource representation.

Naive orthogonal CRUD on (the equivalent of) base tables of a normalized DB design is not a requirement of HATEOAS or REST, and it's often bad API whether or not you are aiming for REST. Its the a strawman every one beats on about REST, but it's got nothing to do with REST.


> More fundamentally, I think it's trying to solve a smaller problem in the face of a much bigger one, you still need to know what the response of any given endpoint is going to be. Just because they've passed me a link, doesn't me I don't need documentation on what endpoint that link points to. I still need to know that the owner is a link to the people endpoint so I can properly parse that result.

Well, yes, you need to know the semantics of the relationship, but not for parsing, because he content-type plus, where needed, internal data within the resource representation more specifically identifying the subtype should tell you what you need to know about handling the representation.

That's HATEOAS.


You absolutely still need docs on the end point.

If you’re interacting with the api then it’s extremely likely you’re not going to write code that will automatically follow reference links and attempt to parse them dynamically.

You will need to know what fields are in the end point how they represent data (iso 8601 for dates? Unix timestamps?).

You’re substituting documentation with asking the api consumer to explore and discover and assume.


You need docs about the media type, which is not exactly the same thing.

But you've documented how your API works anyway, right?


In the model where identifiers are URLs, your example would most naturally look like this: /invoices?customer=/cust/123456. /cust/123456 is an opaque identifier for the client. Since /invoices?... itself is a URL and therefore a URI, it must be the identifier for something—it is the URL of a query result. You can also have a property of a customer that looks like 'invoices: <url>', in which case that URL is opaque. Regarding your second point, it is true that clients still need to understand the entities of the problem domain, but this is still simpler than having to learn the entities and also learn the URL templates that are used to access them, which is the alternative.


There shouldn't be separate endpoints that take an owner's ID. That's bad design. The owner endpoint should contain a list of invoices, ie. links to the invoice endpoint.


Maybe the data model is used for two use cases, one where you primarily access owner entities and one where you primarily access invoices and occasionally do something with their owner. Just because it does not fit some weird, hypothetical prototypical API it's not inherently "bad design". APIs aren't ER models, sure, they're supposed to make sense but they're also supposed to help their consumers perform a task.

And let's not kid ourselves, much of our world runs on APIs that would really deserve the "bad design" handwave, end of the day that's often an aesthetic question.


But the invoice endpoint is likely going to be something like "/customer/(id)/invoice/...". So what did we gain from getting it from the customer description first? (vs getting the customer id from the response and the link pattern from the docs)


You gained the ability to find the invoice. There is no need to have customer id in the invoice url. Just /invoice/invoice_id is enough.


I meant a whole tree of methods. Sure, you can get specific invoices from `/invoice/id`, but you probably still want `/customer/id/invoices` or similar for searching through them. You could use extra parameters for `/invoice/...`, but I think often it's nicer to namespace that. (use case dependent)


If an invoice id is unique across all customers. Which it probably should be, but other ids might reasonably not be.


Do I misunderstand you or do you suggest that the customer object should include the list of all invoices? That does not scale. Imagine if it is something more common like transactions where a user can have made thousands or tens of thousands.


An API should usually prioritize ease of development over faithfully exposing database or architecture internals.

However, you can have it both ways: in the customer response, use

    "invoices": "/customer/7sn2J/invoices"
and have the server 307 redirect to "/invoices?customer=7sn2J", or whatever you prefer.


It can contain a link to the list of all invoices. So you could have /customer/<cid>/invoice which lists all invoices for the customer which are actually just links of the form /customer/<cid>/invoice/<iid>




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: