3 ms·
An interesting design question arrises around nested resources. Google in this doc buys into deep nested structures, e.g. `//calendar.googleapis.com/users/john
by theptip 10y ago
An interesting design question arrises around nested resources. Google in this doc buys into deep nested structures, e.g. `//calendar.googleapis.com/users/john smith/events/123` (from [1]).
I think this pattern is unambiguously sensible when the child objects are strictly scoped under the parent.
But it's less clear how to represent resources that are shared between multiple parents; for example, what if event 123 can be referenced under another user's API resource as well? If we permit `//calendar.googleapis.com/users/bob/events/123`, now we have multiple URLs referring to the same object, and things can get quite tricky in the implementation.
Django Rest Framework strongly discourages (and makes it quite hard to implement) nested resources, FWIW.
I've found that a policy of only permitting one level of nesting seems to be a good balance for shared objects, e.g.
`//calendar.googleapis.com/users/john smith/events/` returns:
```
[
{ url: "/events/123"},
...
]
```
Interested to know how others have solved this problem.
[1]: https://cloud.google.com/apis/design/resource_names https://cloud.google.com/apis/design/resource_names
- combatentropy 10y agoHere's one way to tackle it, if behind your REST API is an SQL database: /schema/table/key So: //www.example.com/calendar/events/123 To address many records, like all that belong to Bob, use the query string instead of purely the path: //www.example.com/calendar/events/?user=bob
- theptip 10y agoThis is in my experience the `standard` design; it does give you a lot of freedom to change what filters you allow, and to stack them. Nobody's going to get fired for this design, and there's a lot of prior art around it to draw examples from. It also has the benefit of keeping the API surface small and clean. But it makes it a bit weird to do HATEOAS; you _could_ do `GET Bob => {events: "calendar/events/?user=bob"}` -- but then you're hyperlinking to a search and not a resource. It also tells less of a narrative in the structure; `user=bob` is just another filter that you can use to apply to the events set. But we get a chance to describe the shape of the data a bit more if we choose to declare an intermediate resource (/users/) and attach some links to it (=>/users/bob/events). Now, if there are ten ways that you need to slice your `events` set, and ?user=bob is but one of them, then scoping a sub-resource /users/events/ isn't that useful/descriptive. As an aside, I think this is where HATEOAS is nice; it makes it very easy to navigate an API as a developer, see what actions are possible at every node, and hopefully learn the intent of the author of the API without having to chew through a set of API documentation. Django Rest Framework's API browser is a great example here.
- joncalhoun 10y agoI prefer the bare minimum approach. That is, if you NEED the user resource to access the event (eg the primary key is like [user ID, event ID]) then nest it in the url. Otherwise if an object has its own ID and could otherwise be accessed independently why not just do so?
- theptip 10y agoThe root of this question is: how do you handle M2M relationships in your API?
- absalyamov 10y agoAs described in https://cloud.google.com/apis/design/design_patterns#get_unique_resource_from_sub-collection https://cloud.google.com/apis/design/design_patterns#get_uni...: //calendar.googleapis.com/users/-/events/123
- theptip 10y agoHadn't spotted that, it's an interesting syntax and I quite like it. But it doesn't cover the case I'm referring to; in their example you MUST return the fully-qualified URL: > shelves/shelf713/books/book8141, not shelves/-/books/book8141 This precludes there being multiple shelves with the same book (sensible in their example, since a book has only one shelf, but not in the example above).
- the8bit 10y agoWhile sometimes this does get tricky with resources that are clearly owned or associated with multiple things, often times a resource only has one real owner and it is unambiguous. It does make moving things a bit weird though, as moving a book between two shelves makes it have a different fully qualified name.
- theptip 10y agoAgree; put another way, sometimes you really do need a M2M relationship.