Where were you when you learned that the backend could do the work of a thousand frontends?
I'll tell you where I was. I was at (what I thought was) the end of (what I thought was) a very simple project where we were asked to create a backend with two relational models that had at least a one-to-many relationship, plus a React frontend to interact with it.
The models that I chose to work with were Jobs and Freelancers in that one Job would have many Freelancers. What I didn't realize at the time, was that my plan to create full CRUD capabilities for my Job model, would A. teach me so much about how the backend and frontend can work together and B. save my project from itself.
Up until this point I have been learning how to be a frontend developer. Our projects had consisted of a frontend that could interact with a static backend. In other words, in order to get any sort of functionality out of your app, you had to make requests from the frontend to predetermined endpoints that we couldn't manipulate.
Having quite a bit of practice at that sort of workflow, I very wrongly assumed that that was it for web development. Even after reading and learning about SQL, Object Oriented Ruby, and ActiveRecord, the idea that I had in my head was that backend tools like ActiveRecord were there to do all the heavy lifting of creating a backend while the end result would still be a static API with familiar but sometimes unhelpful routes.
I am very pleased to report that this is not the case.
After "completing" my project, I reread the directives we were given. I couldn't shake the feeling that I was really missing something - this didn't feel all that different than any of my frontend projects. Sure enough this line in the rubric caught my eye:
Don’t force the front end to do the work of the back end.
I took a look at my code again and did a lot more research. I was most definitely forcing my frontend to do ALL the work. For example, one of the functions I built before this realization focused on what I wanted to happen to the freelancers who were previously assigned to a specific job. I knew that I needed two things to happen: 1. the dependent job_id needed to reset to null and 2. the freelancer needed to be updated to available again.
How would a frontend thinker make that happen if they had not yet seen the light on how to make the backend do this work?
Answer: they would write PATCH requests. Which I wrote out below.
if (deletedJob.freelancers[0]) {
deletedJob.freelancers.forEach(freelancer => {
fetch(`http://localhost:9292/freelancers/${freelancer.id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
is_available: true,
job_id: null
}),
})
.then(r => r.json())
.then(updatedFreelancer => {
freelancersToUpdate.push(updatedFreelancer);
onUpdateFreelancerAfterDelete(freelancersToUpdate);
})
})
}
Exhausting!
At this point I started to think there must be a better way. Did I listen to that gut feeling? No I did not. Onward!
Note that this approach was making a fetch request for EACH freelancer that was assigned to that job we just deleted and each time we were getting a response back from the server. Imagine if there were hundreds of freelancers working on a single project that was deleted? Not good!
Not only does that require a lot of pointless work from the developer, it takes WAY too much time. Especially for larger applications.
So how do we fix this problem?
Before we get there, just because I realized that this WAS a problem, did not mean I fully understood how the server could do this work for you. Despite studying class methods in Ruby and built in ActiveRecord methods, there was still a pretty big gap in my understanding when it came to how the frontend should interact with the backend.
If you really think about it, the server is there to communicate with the frontend. The frontend makes a request for data, the server sends a response. Simple. HOWEVER, what I was really missing is that magic that can happen with retrieving that data before the server sends back the response. Therein lies the solution to my earlier problem.
We don't need to force the frontend to do stuff with our backend data. That's what the backend is there for! Look back at those PATCH requests I was trying to make before and consider this "conversation" between the backend and the frontend:
Frontend: Hello I would like to delete this job please.
Backend: Ok I deleted it.
F: Ok I would also like you to now update the first freelancer on this job.
B: Updated.
F: Ok I would also like you to now update the second freelancer on this job:
B: Updated.
F: Ok now I would also like you too...
B: ...
Horrifying.
Now let's look at how that conversation should really go:
Frontend: Hello I would like to delete this job please.
Backend: Ok I deleted it. I also updated the freelancers associated with this job.
Wow so much better!
So how does that conversation look in the context of this application?
When you frontend makes a delete request to the backend 'jobs/:id' route here's what we'll need the server to do:
- find the requested job
- loop through the freelancers associated with that job and update them accordingly.
- delete the job
- send back the response
Here then is what that looks like in the controller:
delete '/jobs/:id' do
job = Job.find(params[:id])
job.freelancers.each do |f|
f.update(
job_id: null
is_available: true
)
end
job.destroy
job.to_json
end
So. Much. Better.
To take this one step further, ActiveRecord also has a way of telling the server what to do with an associated object's foreign key once that instance is deleted. This takes place inside of the model when establishing the relationship. In my case, I wanted to nullify my dependent foreign key:
class Job < ActiveRecord::Base
has_many :freelancers, dependent: :nullify
end
By including those two words, I can do away with the job_id: null
inside of the update.
WOW! We've come such a long way since we started!
For me this exercise was a gateway into learning how the server could do soooooo much more than I was asking it to do, but I know this is still only scratching the tiniest bit of the surface.
I look forward to learning more about how to manipulate the backend in future projects!
Oldest comments (0)