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 a thread with its posts comments users.
Flat architecture way:
getThreadWithPostCommentUsers(threadUuid) {
const thread = new Thread();
// by default flat, however becomes lasagna code.
// data conscious, layer heavy, flexible.
this.http.request('threads/' + threadUuid).then((threadData) => {
Object.assign(thread, threadData);
this.http.request('threads/' + threadUuid + '/posts').then((posts) => {
thread.posts = posts;
posts.forEach(post => {
this.http.request('posts/' + post.uuid + '/comments').then((comments) => {
post.comments = comments;
comments.forEach(comment => {
this.http.request('users/' + comment.user_uuid).then((user) => {
comment.user = user
console.log(comment.user);
});
});
});
});
});
});
}
- Big amount of layers, lasagna code
- Flexible for data efficient multi-usage.
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:
getThreadWithPostCommentUsers(threadUuid) {
const thread = new Thread();
// by default has nested objects, very simple but inefficient
// not data conscious, not layer heavy, not flexible
this.http.request('threads/' + threadUuid).then((threadData) => {
Object.assign(thread, threadData);
thread.posts.forEach(post => {
post.comments.forEach(comment => {
console.log(comment.user);
});
});
});
}
- Small amount of layers
- Not flexible for data efficient multi-usage.
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:
getThreadWithPostCommentUsers(threadUuid) {
const thread = new Thread();
// by default flat, adds nests depending on resources parameter
// flexible, data conscious, not layer heavy
this.http.request('threads/' + threadUuid + '?resources=posts.comments.user').then((threadData) => {
Object.assign(thread, threadData);
thread.posts.forEach(post => {
post.comments.forEach(comment => {
console.log(comment.user);
});
});
});
}
- Small amount of layers
- Flexible for data efficient multi-usage.
My requests would be flat by default, however my requests have the ability to call on a demand basis for certain nested resources 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