Refactoring a Simple Node.js API

Zurich Okoren
May 8 · 5 min read
Photo by Jay on Unsplash

As an aspiring software engineer I built a pretty simple CRUD API that takes data from the PokeAPI and reformatted the information into more manageable components for future app development.

The Problem

When I first created the API back in November 2018, I thought I did a pretty good job at keeping to conventions and the the code that I wrote made perfect sense to me. If it makes sense to me, then other people can surely make sense of it too.

Who needs comments? Just read the code.

- Me, 2018

Here lies the start of my frustrations in 2019. In fact the code base was bad enough that it was incredibly hard to find bugs in my code when they occurred. Here’s the general file structure for my project for some context.

Seems pretty simple right? Well, the problem is it’s hard to read the nested callbacks. I know it’s in the controllers… EVERYTHING is in the controllers! Additionally, even if I can narrow it down to which route in the controllers is throwing the error, it’s hard to read which line is throwing the error and why. This lead to me refactoring the entire application from scratch to increase legibility and modularity.

The Process

Throughout the process, I had three goals in mind:

  1. Keep the logic in tact. While, the syntax will change, the underlying logic that I used to implement the code needs no modification.
  2. Increase legibility. Mostly, that means add comments to areas that need it and keep functions minimal so that no function is doing too many things at once.
  3. Separate concerns. This means that I need to separate the logic, which I will refer to as controllers from now on, and routes into separate directories. Also, I made sure that each set of controllers is only concerned with one resource.

Here’s what I mean. The following block of code is generally how I structured all of routes:

Example 1

Basically what’s happening is that when users hit the endpoint /team/:teamId/pokemon/:pokeName as described in the first line, the program will then go through the logic I told it to do in the following lines. But what is it doing and why do we need to do this in the first place? For me it’s a bit difficult to tell without comments. Additionally, even if we do figure out what I’m doing, the intent isn’t very clear. I’m sure more senior Javascript developers are either cringing or laughing at this point, but this was what worked for me at that moment in time and I promise I fixed it.

Simplification…For Complexity

As mentioned before, I really tried to simplify my code, but the reason is not just for legibility, but because it allows me to add more complexity to the function without going crazy reading endless nested callbacks. That said here’s the same function but with just a little bit more bells and whistles:

Example 2

I swear it’s the same function! At least, it started off as the same function. Besides the comments, I also re-implemented the function as an async (short for asynchronous) function due to the fact that I’m making an external API request that take a variable amount of time to complete. I won’t go too in depth in async functions, but freeCodeCamp’s article on async/await is a great resource to learn more about it. Another thing you might notice is that we’re missing the endpoint we see in the first line of Example 1.

Separating Concerns

This takes us back to the third objective in this refactor process. I was taught that files should be grouped based on functionality. And while the initial implementation was passable, I didn’t quite feel satisfied with the results. Here’s my refactored file structure:

Compared to the initial file structure, we can see that there three more folders. The only one we’re concerned with here is the routers folder. This folder is what contains the all of the endpoints accessible in my API. Notice that the folder contains three files in it. Each file is a collection of endpoints related to a specific resource. Here’s what the the pokemons.js, which contains all the endpoints that make external calls to the PokeAPI, file looks like:

Example 3

Remember that async function we had before? It actually lives in a file inside our controllers folder. If we take a look at line 2 in Example 3, we’re telling our program to go into the controllers folder and grab the file that has the functions we need to use as callbacks in our router requests. In this case the file is called pokemonControllers.js . I chose to set it up this way so that I can isolate all routes that require resources from external sources into one place. Similarly, each file in my routers folder only concerns itself with one resource like user accounts and pokemon teams.

Conclusion

Throughout the entire refactor process I did not touch the underlying logic that the original app used. Instead I was able to condense the syntax and add more functionality overall.

The biggest impact to this refactor is that it makes it easier for me to add or modify features in the future. What started off as a simple Node.js api that simply made external api calls is now more robust and feature-full.

If you’re curious as to what the project looks like, here’s a link to the project Github repo.

Thanks to Faith Chikwekwe

Zurich Okoren

Written by

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade