I recently had thoughts on handling the flat vs nested architecture when designing a RESTful API.
Would there be anything wrong with having a system where your nested resources can be specified within a query parameter of the API request? This is only needed in certain complex situations, otherwise you would just access it regularly through collections/instance/collections/instance.
Example:
Lets say we need to get all the users who commented on all posts of a single thread.
Flat architecture way:
getCommentUsersOnAllPostsFromThread(threadUuid) {
const users = [];
// by default flat, however becomes lasagna code.
this.http.request('threads/' + threadUuid + '/posts').then((posts) => {
posts.forEach(post => {
this.http.request('posts/' + post.uuid + '/comments').then((comments) => {
comments.forEach(comment => {
this.http.request('users/' + comment.user_uuid).then((user) => {
users.push(user);
});
});
});
});
});
}
- Data efficient
- Big amount of layers, lasagna code
- Flexible
This feels the most encapsulated and straight forward. However it starts to become a bit redundant and unfavorable when you deal with alot of relationships.
Nested architecture way:
getCommentUsersOnAllPostsFromThread(threadUuid) {
const users = [];
// by default has nested objects, very simple but inefficient
this.http.request('threads/' + threadUuid).then((thread) => {
thread.posts.forEach(post => {
post.comments.forEach(comment => {
users.push(comment.user);
});
});
});
}
- Not data efficient
- Small amount of layers
- Not flexible
The nested way is nice from a convenience perspective but isn't very flexible if you care about minimizing data.
So my idea was to combine the two, where I can reap the benefits of a simple flat architecture, but also have the benefits of loading nests in one request.
Flat and nested hybrid way:
getCommentUsersOnAllPostsFromThread(threadUuid) {
const users = [];
// by default flat, adds nests depending on resources parameter
this.http.request('threads/' + threadUuid + '?resources=posts.comments.user').then((thread) => {
thread.posts.forEach(post => {
post.comments.forEach(comment => {
users.push(comment.user);
});
});
});
}
- Data efficient
- Small amount of layers
- Flexible
My requests would be flat by default, however my requests have the ability to call on a demand basis for certain nested relationships without creating a bunch of custom routes and being data efficient.
Authorization would still be done on the serverside, allowing only certain resources depending on request.
Would this be a bad practice/design choice? What are your thoughts? Is there something out there for this and I am reinventing the wheel?
Thanks