HydraCG / Specifications

Specifications created by the Hydra W3C Community Group
Other
138 stars 26 forks source link

Question about HATEOAS shortcuts #189

Closed ghost closed 5 years ago

ghost commented 5 years ago

In some cases we have to save somehow the actual client state, which is bound to specific REST resources. E.g. we selected a product from the catalog and add to the user favourites, or we use an SPA REST client and we want to link it with https://example.com/our/client/catalog/product/123. In those cases the client needs to know which links to follow to reach the a certain client state again, e.g. main -> catalog -> next-page : until-we-reach-product-with-id(123) -> product(123).

We have 2 problems here:

  1. Using pagination to find the link to the product having the right id takes forever and uses way too many resources, so we need some sort of shortcut: main -> catalog -> find-product-by-id(123) -> product(123). Normally we would not have that shortcut. Ofc. we can have search, but a typical user does not search by product id, but by criterias like color:red, size:medium, etc. So we need extra link(s) in our REST API just to support saving certain client states by collections.
  2. The client needs to know about some sort of REST sitemap in order to reach the specific client states in these cases. If we want to build our client on top of a REST browser it would be a good feature to support this with some sort of sitemap format the REST API could provide. So the client would not need to know that it should follow main -> catalog -> find-product-by-id(123) to get to the product(123) link, because it could get that info from the sitemap. All it needs to know that it needs to find and follow the product(123) link to get to the client state it needs to reach. We could even use a REST browser and just pass the operation we want to use and the input and it could discover the API and find the proper link on its own.

I am curious if somebody is working on a recommended solution for REST sitemaps in this topic, or if you have a better idea for these cases.

tpluscode commented 5 years ago

but a typical user does not search by product id

Indeed. I find this statement contradict the actual example above which assumes you are looking for product-with-id(123). If that is the desired outcome then maybe a search by the id is what your API needs?

So we need extra link(s) in our REST API just to support saving certain client states by collections.

Could you elaborate? I don't see how the collection would save client state? Seems to violate statelessness of REST.

tpluscode commented 5 years ago

Regarding the actual problem you're trying to solve,

In those cases the client needs to know which links to follow to reach the a certain client state again

I think you're assuming that the client always has to start from the entrypoint. Thinking in terms of web browser analogy, you don not always have to start from scratch. The web browser can keep states between sessions as open tabs (implicit) or bookmarks (explicit).

Your REST-driven application can also do the same. It would be the client's responsibility to keep track of its state so that the right requests can be made to rebuild the UI. This should have no bearing on the API itself, as it doesn't really care.

ghost commented 5 years ago

@tpluscode I thought you always have to start from the entry point, because the URIs can change. So if you save an URI, then it can break later. I'd rather save the id and get the actual URI template for it from the API.

alien-mcl commented 5 years ago

Entry point's URL also can change - with hydra the only stable URL would be an API Documentation's URL that should be provided by hydra powered API in each response's header. API documentation should provide an entry point. That's why it is possible to start with any URL.

As for the sitemap like solution - I believe API documentation is the closest one - it's the place where client could obtain some details about the API on an ad-hoc manner. Still, it is up to the server on what's inside.

Regarding the state - it's up to the client. Server just serves resources. That's why modern browsers are quite stuffed with features related to state storage (local storage, etc.)

ghost commented 5 years ago

@alien-mcl Sure, it is up to the client, but what if the client cannot figure out how to reach a certain state on its own?

Maybe my description of the problem was not clear enough. I try it again. For example you find product 123 on the 12th page of the catalog, it has a link of https://api.example.com/v1/catalog/product/123. You check the product details with the Catalog.getProduct({"productId": 123}) (where the catalog is a Hydra Resource, getProduct is an operation, productId is a property of the APIs vocab). You like the product so you save it in the favourites on the REST client. So the client will know that next time you want to reach the product from the favourites you need to use the Catalog.getProduct operation again with the {"productId": 123} input. Meanwhile the URIs change and you got https://api.example.com/v1/product/123 for the same resource and the old URI no longer works. How do you get a working link with the new URI for your favourite product? As far as I understand you need to find somehow the Catalog first and get some sort of searchByProductId(123) operation, which will give you a link to the getProduct({productId: 123}) operation. The origional way you found that link was totally different. How do you solve this problem based on the documentation automatically? Or should I just save the link that can break and respond in these cases with a "sorry pal, spend your money somewhere else" error message? Maybe I misunderstood your answer and you meant the API documentation for the developers, not the API documentation for the machines described with Hydra vocab.

tpluscode commented 5 years ago

with hydra the only stable URL would be an API Documentation's URL

Are you 💯 percent sure about that? I've always assumed that the entrypoint should be the stable point of contact.

You check the product details with the Catalog.getProduct({"productId": 123})

This already presents a problem. The client should have no understanding that 123 is some kind of identifier. The only real identifier is the URL.

Thus, once you bookmark https://api.example.com/v1/catalog/product/123, you can return to it simply by requesting that very resource. You do not need to find somehow the Catalog first and get some sort of searchByProductId(123) operation

Meanwhile the URIs change [...] Or should I just save the link that can break and respond in these cases with a "sorry pal, spend your money somewhere else" error message?

Now this is not really the client's problem to solve. I'd say that the responsibility of a well-designed API is to provide redirects, at least for a certain period following the change. You could then monitor for incoming requests and once the requests for the "old" URLs drops below a "low enough" threshold, you would then remove them completely.

alien-mcl commented 5 years ago

Are you 💯 percent sure about that? I've always assumed that the entrypoint should be the stable point of contact.

It can be moved to i.e. another sub-domain, or port or whatever. I wouldn't rely on it as immutable. API changes over time, some times it happens that one API is sucked into another.

This already presents a problem. The client should have no understanding that 123 is some kind of identifier. The only real identifier is the URL.

The resource's URL should be bookmarked - this way you don't need understanding anything else. Just obtain resource under that URL and you're all set.

Maybe I misunderstood your answer and you meant the API documentation for the developers, not the API documentation for the machines described with Hydra vocab.

It is for both, developers and machines. Actually it would be also for users - you can use API documentation elements to provide human readable UI. As for bookmarks - API documentation is not that necessary as you can see. Resolving a bookmarked URL may be enough. Only in case of an issue with resolution (i.e. 404 Not Found), server should come with a response with API documentation included so you can fall back i.e. to home page or as you wrote - provide some error message.

ghost commented 5 years ago

@tpluscode

This already presents a problem. The client should have no understanding that 123 is some kind of identifier. The only real identifier is the URL.

I don't remember writing that the client knows that 123 is an identifier. All the client knows that the URI is build with {productId: 123} and with an URI template. It assumes that the parameters of the template does not change when the URI changes, just the URI template itself. But nice catch, because that assumption does not have to hold. If so, then I think the only option to find that resource again is having some sort of service that converts to old link into the new one. I think that should be a bookmarking service sort of.

Now this is not really the client's problem to solve. I'd say that the responsibility of a well-designed API is to provide redirects, at least for a certain period following the change. You could then monitor for incoming requests and once the requests for the "old" URLs drops below a "low enough" threshold, you would then remove them completely.

Yes, I think that is a good solution too. I think even better than having a service for this. Still I don't feel right. I mean in both cases it would be nice to notify somehow the client that the link is deprecated. Is there any option for that?

ghost commented 5 years ago

@alien-mcl Hmm I think a fairly good solution would be redirecting for a while, after that returning 404 with the new link in the error message and finally returning only 404. Any opinion about this?

tpluscode commented 5 years ago

I don't remember writing that the client knows that 123 is an identifier. All the client knows that the URI is build with {productId: 123} and with an URI template

Yes, that is precisely the point I was referring to. How is that 123 ever relevant going forward? The client best only remembers the URI and should not car about the template and its parameters any more.

I mean in both cases it would be nice to notify somehow the client that the link is deprecated. Is there any option for that?

Indeed I think that the sunset header which @dret proposes is the solution you're looking for.

ghost commented 5 years ago

@tpluscode Thanks! That's exactly what I was looking for.

dret commented 5 years ago

On 2019-04-18 21:07, Tomasz Pluskiewicz wrote:

I mean in both cases it would be nice to notify somehow the client
that the link is deprecated. Is there any option for that?

Indeed I think that the |sunset| header https://tools.ietf.org/id/draft-wilde-sunset-header-03.html which @dret https://github.com/dret proposes is the solution you're looking for.

sunset is about saying that resources will stop responding (i.e., more than just deprecated). we're now working on a spec for indicating that resources are deprecated (i.e., still working but not the recommended version anymore).

https://tools.ietf.org/html/draft-dalal-deprecation-header

-- erik wilde | mailto:erik.wilde@dret.net | | http://dret.net/netdret | | http://twitter.com/dret |

ghost commented 5 years ago

@dret Yes, I closed this too early.

I read the description of the sunset header and it is good only if I want to remove the resource and I should write 410 gone after that.

I am not sure if the deprecation header is a solution here. I mean the resource is still available, just the URI changed. Afaik you can have multiple URIs for the same resource, so changing the URI does not mean that you have removed a resource and added a new one. Actually if we keep the old URI for a while and have a new URI too, then we have a scenario where 2 URIs identify 1 resource.

I think I'll stick with 301 moved and a redirection and later change it to 404.

dret commented 5 years ago

On 2019-04-18 21:30, Jánszky László Lajos wrote:

@dret https://github.com/dret Yes, I closed this too early. I read the description and it writes 410 gone after time is out. It should be 301 moved in this case because the resource still exists, only the URI changed. So I think in this case the redirection might be a better option.

yes, if the resource simply moved, neither Deprecation nor Sunset are a good idea. these are used to indicate that the resource is nearing its end-of-life, or will disappear.

-- erik wilde | mailto:erik.wilde@dret.net | | http://dret.net/netdret | | http://twitter.com/dret |

ghost commented 5 years ago

@dret Thanks, still it was useful, I did not know about those headers. I think we can close this.