Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Using nested paths vs. flat ones for API resources
This is based on a code review discussion that I had with a colleague about the way I have designed the resources paths for an ASP.NET Core controller that is currently consumed only internally (by a SPA).
The paths were flattened (e.g. GET /api/productreview/{reviewId}) and my colleague argued that they should be nested (e.g. GET /api/product/{productId}/review/{reviewId}.
My rationale for choosing the "flattened" version was the following:
-
any functionality should get the minimum required input to do its job. Any redundant information requires extra checks (i.e. what happens if
reviewId
does not actually belong to theproductId
?) -
the endpoints are only used by our own SPA and thus only queried by the SPA and by us via the SwaggerDocs
From a REST perspective, I agreed and changed them, since the ids are integer values and we do not risk having very long URLs.
I am wondering if there is a guideline related to choosing hierarchical API paths over flattened ones based on various criteria such as the way the API is consumed and possibly other criteria.
1 answer
Actually, while REST mandates a great many things, it does not constrain the structure of URLs. To wit: The paper that coined the term "REST" describes resource identifiers as black boxes, and makes no reference to their internal structure. In fact, if we take a strict reading of the REST architecture, we'd have to follow the HATEOAS constraint, which implies that the structure of URLs must not matter to clients :-)
Granted, most sane people ignore the HATEOAS constraint, since that constraint is only useful when using a generic client, but your everyday angular app is specific to the API consumed. In this relaxed REST architecture, the app may have prior knowledge of URLs and their structure, but since this affects only the app, and the app is often done by a same team, this is an internal matter that does not have any deep architectural impact.
So there is nothing at all in REST that says that URLs must be nested as deeply as possible. You can express relationships by nesting, but you can also express them by links (or plain object identifies, if you don't follow HATEOAS). In fact, in general this is necessary because URLs are hierarchical, but relationships aren't necessarily tree shaped. So REST must offer both hierarchical resources, and links, and it is fine to use either of these approaches.
The one area where REST could be construed to say something about the structure of URLs is the interaction with HTTP Caching, if that matters to you. By default, a POST will invalidate the cache of the URL being posted to, but not the cache of other URLs. Therefore, the default is appropriate if each resource is available via GET under exactly one URL. For instance, extending your example, it would be rather bad form to return the same data from
/product/123/review/345/author
/product/127/review/349/author
But that's what I already hinted at: Nested paths might be ok for composition, but not for arbitrary associations.
That is, in general you'll want to use links, but you can also use nested paths if the relationship is a composition. In general, there is no technical reason to prefer one over the other, so it comes down to convenience and conveying semantics to the human calling your API.
That is, you could use a nested path to emphasize that the relationship is a composition, or to make your API easier to organize, or to secure it more easily (for instance if you use declarative access control based on URL patterns). On the other hand, it may be inconvenient for the caller to have to remember and provide the product id in addition to the review id. Barring any compelling reason, I'd default to KISS, and use the simpler URL format.
0 comment threads