Live data from Hacker News

Ask HN: Nested Resources in REST/HTTP API URLs?

news.ycombinator.com

51–60 of 72 posts

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#51
post #3

Just do: - /organizations/:id - /blogs/:id - /sections/:id - /threads/:id - /comments/:id Why? What determines how resources are related are links, not patterns in the URL. It's a graph, the URLs are just nodes, the links are what connect them. If you _want_ to have some sort of hierarchy in the URL, you can redirect: - /organizations/123/blogs/1234 -> /blogs/1234 Then each resource expresses how it is related to oth…

That's a poor advice. APIs are consumed by humans first. Machines don't care about your structure. HATEOS is the part that actually doesn't matter in practice.

[deleted]

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#52

The Github API [0] is a good place to look- it seems like they thought a lot about their hierarchies. They do things like /orgs/ORG/repos /repos/OWNER/REPO/issues In other words, GH scopes their endpoints usually one maybe two layers. I think conceptually that makes a lot of sense [0] https://docs.github.com/en/rest/repos/repos#list-organizatio...

IMO a big part of their hierarchies can be attributed to rails default resource routing structure

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#53
I would go for the hierarchical urls, like:

- /organizations/:organizationId/blogs/:blogId

- /organizations/:organizationId/blogs/:blogId/sections/:sectionId

- /organizations/:organizationId/blogs/:blogId/threads/:threadId/comments/:commentId

I do this because it expresses the structure and the constraints of the data. A blog cannot exist without being linked to an organisation. And then you typically have the following CRUD endpoints:

- /organizations/:organizationId/blogs, with GET and POST to retrieve a list of blogs belonging to an organization and to create a blog for an organisation

- /organizations/:organizationId/blogs/:blogId, with GET, PUT and DELETE to retrieve, update and remove a blog

The fact that you always have the :organizationId in the url likely means that you can easily verify authorization, because I assume you would have a link between the user and an organization and maybe that link would already be available in the access token.

I don't think it is a problem that the urls are heavily nested. The urls are not supposed to be manually entered, but are using from within an application. I like the use of HATEOAS links, because I don't really like the frontend trying to assemble urls by itself.

Also note that next to the hierarchical links, you might need some extra for search that are not hierarchical. For example, say that there is a need to search blogs across organizations. This could be provided using the following:

- /blogs?description=test&language=en

You can provide a search endpoint in the root for blogs. This endpoint could then return some kind of blog object with a reference to the actual blog location (like /organizations/34/blogs/11). When you use this approach, the frontend has no need to assemble urls and the structure or complexity of the url does not matter. Do note that the frontend still needs some generic "start" urls like the blogs search endpoint.

Additional advantage (like others have mentioned), is that with the hierarchical urls it becomes more difficult to "guess" an url. Altough, if you want to properly protect against that, you should transform the ids before putting them in the url and transform them back when reading them out of an incoming url, for example using symmetric encryption with a secret only known in the backend.

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#54
post #3

Just do: - /organizations/:id - /blogs/:id - /sections/:id - /threads/:id - /comments/:id Why? What determines how resources are related are links, not patterns in the URL. It's a graph, the URLs are just nodes, the links are what connect them. If you _want_ to have some sort of hierarchy in the URL, you can redirect: - /organizations/123/blogs/1234 -> /blogs/1234 Then each resource expresses how it is related to oth…

That's a poor advice. APIs are consumed by humans first. Machines don't care about your structure. HATEOS is the part that actually doesn't matter in practice.

Both machines and humans can easily follow links, that's precisely why hypermedia was invented.

The part for humans is the HTML form I'm typing on. Humans _can_ type in HTTP/1.1 non-chunked requests by hand, but I don't see it that often.

For developers, you might think that the URIs are "the UI" of an API, but they were not designed to be used in that way.

We were meant to use the HTML rel= attribute to inform our machine clients on how to navigate links. We still do, sometimes. rel=stylesheet is a vestigial trace of that. rel=nofollow still informs clients of irrelevant URLs to this day.

We use HATEOAS all the time, we're just not using the standards designed to express them. A complete API schema (what seems to be the cool thing these days), in the eyes of REST, is nothing but a giant complex hypermedia form written in an unspecified media type. The "blog_id" is nothing but a lofi link, and so on.

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#55
post #32

A problem with nested URLs like /organizations/:organizationId/blogs/:blogId/sections/:sectionId is that when you are manipulating a section from the frontend, you don't only need to keep and pass around their sectionId, but also their organisationId and blogId everywhere. Often you'll already have those, but sometimes not and it'll be annoying, you'll have to make more complex code with functions taking more paramet…

> From the backend, you'll need to check that all those ids are consistent and match.

Or you don't check and just discard all the superfluous parameters, I've seen that too unfortunately.

All your points are valid of course :)

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#56
post #3

Just do: - /organizations/:id - /blogs/:id - /sections/:id - /threads/:id - /comments/:id Why? What determines how resources are related are links, not patterns in the URL. It's a graph, the URLs are just nodes, the links are what connect them. If you _want_ to have some sort of hierarchy in the URL, you can redirect: - /organizations/123/blogs/1234 -> /blogs/1234 Then each resource expresses how it is related to oth…

This is good for fetching a single item. What if you want to list resources? Eg. List all comments of a blog? It has to be GET /blog/:blogid/comments Then, how about creating a new comment? It'll be POST /blog/:blogid/comments When both of these have the blog's hierarchy in the URL, should we take it away when fetching a single item? Or the stay consistent, we should simply do: /blog/:blogid/comments/:commentid It's…

List all comments of a blog: GET /blog_comments/:blogId

Creating a new comment: POST /comments (blog_id, author_id, category_id all in the post body)

Single comment: GET /comments/1234

You can pretty much follow all the same normalization rules you would for a SQL database. /blog_comments is a kind of query in a virtual N-to-N resource that maps blogs to comments, get it?

Consider for example, "all posts that this particular author commented on". That would be insane to put into a hierarchy URL. For a flat one, you can just:

GET /commented_by/:authorId

Boom, done. Sometimes, you'll need more than one id, of course, but that does not imply "nesting". Consider "all posts where these two authors interact on comments":

GET /discussions_involving/:authorId1/:authorId2

There is a cap in what you can do with a hierachy, and the web is not a filesystem with folders, it's a graph with unlimited possibilities.

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#57
post #55
post #32

A problem with nested URLs like /organizations/:organizationId/blogs/:blogId/sections/:sectionId is that when you are manipulating a section from the frontend, you don't only need to keep and pass around their sectionId, but also their organisationId and blogId everywhere. Often you'll already have those, but sometimes not and it'll be annoying, you'll have to make more complex code with functions taking more paramet…

> From the backend, you'll need to check that all those ids are consistent and match. Or you don't check and just discard all the superfluous parameters, I've seen that too unfortunately. All your points are valid of course :)

Yup. Actually though about that possibility while writing this.

Quite like I would not use a function argument if I don't need it. So I would avoid putting these parameters in the function signature in the first place. It just leads to confusion at best, and there's a good reason why many compilers emit warnings for this.

I believe a function should only take the minimum it needs, and I believe APIs should not be different in this matter.

Redundancy is good when you actually need it, and not quite when you don't.

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#58

It doesn’t actually matter unless your API is expected to be consumed without any particular entry point. If you can expect users to enter at specific points, you can represent state as hypertext and give them links (URLs) rather than ids. They don’t need to know or care about your URL patterns, they just need to follow the hyperlinks you provide. You can do the same even if some entry points are known, and then I'd…

> They don’t need to know or care about your URL patterns, they just need to follow the hyperlinks you provide.

This is REST. As a backend dev I've attempted this several times, and the consumers of my API never cared about the hyperlinks.

I've tried explaining that the 'delete' hyperlink is only going to be present when the user is authorized to delete the resource, and isn't that wonderful and making your life easier? It turns out, (my) API consumers want to hardcode URLs on their side and have the payload include a can-delete flag. Same across several companies.

I've since mostly given up on making interfaces restful. Anyone has experience with this going the same or differently?

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#59
post #18
post #3

Just do: - /organizations/:id - /blogs/:id - /sections/:id - /threads/:id - /comments/:id Why? What determines how resources are related are links, not patterns in the URL. It's a graph, the URLs are just nodes, the links are what connect them. If you _want_ to have some sort of hierarchy in the URL, you can redirect: - /organizations/123/blogs/1234 -> /blogs/1234 Then each resource expresses how it is related to oth…

I'd do it this way as well for most cases. Complicated URLs are hard to remember and annoying to share. The one benefit of the nested URL is that you get is of being able to "inspect" what organization or section or comment a piece of content belongs to. This is probably a rare edge case and something you should only implement if it makes your system much more usable, for some reason. e.g. I'd probably rather have po…

A cool way of reaping benefits of both approaches is to separate client side and server side URLs.

The thing after the '#', which has been called many things in the past (hash fragment, I guess), never goes into the server, so you can play with it using JavaScript to make it meaningful to users, while keeping it simple and flat on the real HTTP requests.

Re: Ask HN: Nested Resources in REST/HTTP API URLs?

#60
post #27

Why reinvent the wheel? Just use a common standard... I prefer https://jsonapi.org/ .

jsonapi solves some problems, and introduces so many more. As a standard intended to reduce bike shedding, I have never had as many bike-shedding discussions as I have with jsonapi. Include syntax, plural vs single resource naming. sorting syntax... the list goes on. And worst of all... you cannot represent an API backed by jsonapi with swagger/openapi... a more common standard. If you want jsonapi, you probably real…

Well, that might be true when implementing from the scratch, but using a standard often also means, that someone has implemented a well known library to get rid of the boilerplate and basic decisions.

I personally often use jsonapi.net[1], a C# implementation of JSONAPI. This supports OpenAPI/Swagger with swashbuckle, has a very good filtering implementation and together with Orbit.js[2] it is pretty much without having to decide too many things...

[1]: https://www.jsonapi.net/

[2]: https://orbitjs.com/

Post reply on HN