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

This falls down as soon as it makes a fundamental misunderstanding of what makes a REST api into a REST api.

It gives this as a ‘bad’ example:

   GET /v3/application/shops/{shop_id}/listings/{listing_id}/properties
With the justification that “The {listing_id} is globally unique; there's no reason for {shop_id} to be part of the URL. “

No the point of the API is that /v3/application/shops/{shop_id}/listings/{listing_id}/properties is a globally unique identifier. Your belief that parts of that id have global meaning outside the context of that identifier is irrelevant - that path is the identifier for the resource.

And having hierarchical paths is useful because you can do things like manage permissions on parts of the hierarchy - users might have permission to check listings in certain shops and we can characterize that as them having permission on /v3/application/shops/{shop_id}/listings/*.

Directory structures of resource identifiers are good and logical and not a ‘bad’ API design practice at all. You might as well argue the UNIX file system is a bad design because all the files have a unique inode id so paths are completely unnecessary.




You apparently have not actually used Etsy's API.

No, the shop id is not in fact part of the globally unique identifier of an Etsy listing, and the properties are not dependent on the shop. Etsy listings have a 1:N relationship with Etsy shops.

The API was a mistake, which they are slowly correcting - they've already changed:

    GET /v3/application/shops/{shop_id}/listings/{listing_id}
to:

    GET /v3/application/listings/{listing_id}
...and I presume they will eventually change the rest of the listing-related endpoints over time.

Managing permissions using the hierarchy of a URL is silly at best, dangerous at worst. The first thing any attacker will do is plug in an alternative shop id and see if it grants access to the non-permitted listing. If permissions are attached to the shop (and for Etsy, they are) the server needs to load the listing, figure out the associated shop, and then check permissions. The client cannot be trusted to provide the correct shop id, so there's no point in asking for it.


That’s a critique of Etsy’s API not of good REST resource identification.

No plugging in a shop you have permission to doesn’t work if your resources are hierarchical any more than plugging ~/passwd let’s you read /etc/passwd because you have read access to your home directory. Those are different resources and one of them exists and is locked down and the other one doesn’t exist.


> Managing permissions using the hierarchy of a URL is silly at best, dangerous at worst.

Or perhaps what you call silly is just you being unaware of what you don't know. There are valid cases to handle permissions using the structure of URLs. As well, the danger you allude to comes from handling it naively. Even the hypothetical attack you suggest might be among the first thing any non-tech savvy person might think of trying.

The scenario you're describing above is simply one of dealing with redundant information in a situation where inferring the whole from the part is not detrimental (for the platform). A case can certainly be made that with that simplification, some optimization opportunities are also lost. Perhaps Etsy doesn't need them. Others might.

> The client cannot be trusted to provide the correct shop id.

The client cannot be trusted period. If I provide a signed cookie that contains a list of authorized shops and they return something else, good thing that cookie is signed. Also good thing the cookie contains the shops, no need to touch the disk if the URL doesn't match the list.


You apparently haven't read Fielding's paper. Etsy isn't doing it right. If you read Fielding's paper, OP's point is correct. The whole URL is a resource.


The author gives a reason for that recommendation

> [having shop_id in the URL] inevitably causes problems when your invariant changes down the road - say, a listing moves to a different store or can be listed in multiple stores.

Basically the choice is between having a perpetual unique URL to a listing or multiple ones, maybe valid at the same time and some of them maybe invalid in future, when a listing is removed from a shop.

A visitor with a valid unique listing id will always be able to look at the product. If there is a shop id in the URL that URL might become invalid and the visitor loses access to the product and had to search for it again, adding friction. With the global unique is the visitor will discover that the product is offered by another shop (maybe a new one from the same tenant?) which is usually not important.

Permissions for the listing could be handled by matching the shops a user has access to with the shops the listing belongs to.


That’s what 302 responses are for.

REST does not mean ‘parameters in the path not in the query string’.


I don't understand.

Suppose that we have

  /shops/1/listing/1
  /shops/2/listing/1
where listing 1 is an id local to those shops, they are two different listings. What happens to those URLs when those shops remove those listings? Both of them should return 404.

Then the listing appears in shops 3 and 4.

  /shops/3/listing/1
  /shops/4/listing/1
If we have four different records in the listings table of the database there is usually no way to relate those listings, unless we inspect all the records after creates and updates looking for exact matches. So we can't redirect.

Let's say that those listings 1 are globally unique ids. There is only one record for them in the database. When that listing is removed from shops 1 and 2 and later appears in shops 3 and 4, which shop do we redirect the original URL to? 3 or 4? We have only one choice and if we redirect to shop 4 the owner of shop 3 won't be happy and viceversa. We can add some reference in the JSON response that will look like 302 Location: /shop/4/listing/1 with a body including {"also_sold_by": [3]}' but again, why arbitrarily pick the main URL?

But if we have a URL like

  /listings/1
we can return a reference to shops 3 and 4 in its JSON response. Everybody is happy.


I don’t know if it is a usecase for etcy.. but In the first scenario doesn’t it make having different prices for a listing easier, because you will have a “shop item” table where you tell the system which items are in which shop, and can attach shop specific info.

So you have: |shopid|listingid|itemid|

The item is global and the listing is local to add shop specific info, or even whether that shop carries that item?

Of course this might not be a valid use case or I may misunderstand then meaning of listing.


You're sacrificing usefulness for purity. Never a good bet. I agree with the author.


I’m not arguing for purity I’m arguing that these guidelines are not good guidance for designing ‘REST APIs’.

If you are designing a ‘REST API’ you have already committed to ‘purity’. If you follow this guidance you are not designing a REST API you are designing a JSON over HTTP api with parameters in the query string.




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

Search: