username: User -> one String password: User -> one Password dateJoined: User -> one Date
concept: chirp (aka post)
author: Post -> one User dateCreated: Post -> one Date content: Post -> one Content dateModified: Post -> one Date
concept: chirper (aka friend)
user: Friend -> one User friends: Friend -> set User
concept: nests (aka circles)
name: Nest -> one Name creator: Nest -> one User members: Nest -> set User posts: Nest -> set Item
concept: group times
creator: Time -> one User group: Time -> one Object startTime: Time -> one Hour endTime: Time -> one Hour
User Post Friend Nest[Post.Post] Times[Nest.Nest]
One of the tradeoffs I struggled with was either implementing a lot of middleware, but exposing more endpoints with my collections, or creating a code base robust enough to handle some common errors and exposing less endpoints with my collections. I ended up choosing the later, since I decided to focus on making my endpoints only expose the most necessary functionality needed to fully implement my operating principles. For example, I prevent users from being able to
GET all freets, all users, all times, and all nests. Instead, I check that a user trying to get all of a creator’s nests can only be the creator themselves (i.e. only the creator of the nests can see all of their nests). I also struggled where to place which middleware, like checking that the creator of a nest is a valid user. To prioritize modularity, I placed any user checks in the user middleware, nest checks in the nest middleware, and so forth.
In addition, in terms on implementation, I realized there was some tension between my code modularity and actual synchronizations. For example, I debated whether to sync the deletion of users and posts to my nests and friends. I decided to make a cohesive app, it was important for me to sync the deletion and also sync creations: when a user is created, a friend object to the
FriendCollection is also immediately made. These synchronizations are also an example of how, rather than exposing the
POST endpoints to users, I created a code base that would automatically handle creation/deletion to prevent duplicates, input errors, malicious attacks, etc.
In terms of my routes, specifically for friends, I had to decide what routes make the most sense in terms of RESTful design. I ended up choosing to create
DELETE routes to represent adding and removing a friend, but technically, in my implementation, these are just updates to a user’s friend object.
Lastly, some of the tradeoffs I noticed while working on were deciding what to delegate what concepts to actually implement in my backend versus what concepts to delegate to my frontend. I ended up deciding not to implement my profile, feed, or filtering concepts since they can represented as the data from calls to the endpoints of user, nest, post, times, and friend.