API Wrangling
Tags: javascript, beginners
Published: 2022-07-25 19:11:00

My first [project](https://github.com/theNicomachean/brew-dawg) called for creating a javascript frontend interface that employed some API. I originally planned on using the Marvel Comics API, but that would have taken longer than was reasonable for this project. So, I went with the Open Brewery DB, a database that lists all the breweries in the United States. 

The API is simple enough, and provides you with endpoints that enable searches for breweries by state, city, or type--among others. However, I quickly encountered an issue when I wanted to return results from all the pages returned from an endpoint. If I output and rendered everything the endpoint returned, I would have ended up with blank listings--since the last page in a return may only have 5 of 50 possible results (per page). What to do? What to do?

I figured the best way would be to filter out any result that was blank/null. That way, I could request all 50 pages that the API can return, and then remove any null objects. It was a bit ugly, since I had to manually set the number of pages it would look through, but it does seem to work for getting to a Minimum Viable Product (MVP). So, how does it work?

The system is able to return results for up to 50 pages. However, very few results have more than 3 pages, let alone 20 or 30. One of the arguments I passed to the `Promise` function is a map of integers. Each integer represents a page number returned from the endpoint. I set the max at 15. Following, I used interpolation to enable the `map` function to insert the page number in the Open Brewery API endpoint like this:

```
fetch(`https://api.openbrewerydb.org/breweries?${usrInput.value}&per_page=50&page=${id}`)

```

the `usrInput` interpolated string takes as an argument the user's search term. The next step was to get rid of any empty objects returned by the API. The following code filters out all objects with 1 or more returned items:

```
let results = data.filter(item => item.length > 0)[i - 1];

breweries.push(results.filter(Boolean));

```

So, we go from returning pages of empty objects to returning and rendering only those objects that have data! Once I figured that out, the rest was fairly straightforward. Overall, I had a good challenge, and a bit of fun figuring it out!

## Make it Spiffy

The project guidelines required that we code three event listeners. So, in addition to the event listener on the form submit button, I created one for liking a brewery, and one for a modal box that brings up with a link to the brewery's webpage.

The modal was made using Twitter's Bootstrap 5. I won't go into that since there's plenty of information about Twitter Bootstrap out there on the interwebs. The like button is a bit more interesting to discuss anyway.

For the like button I employed Font Awesome's icons. I thought about using a free one, or one that was open source, but they don't have near the number of icons (and icon options) as Font Awesome does.

After rendering the html elements search results, I coded grab all the hearts ( `let hearts = Array.from(document.querySelectorAll('.fa-heart')); ` ) which were created and added to the page via javascript. Each heart can then be transformed to function as a 'like' button. The following code enables the 'like' functionality:

```
hearts.forEach(heart => {                         
  heart.addEventListener('click', (e) => {
    let classList = 
    Array.from(heart.classList);
      if (classList.includes('fa-solid')) {
        heart.classList.remove('fa-solid');
        heart.classList.add('fa-light');
      } else {
        heart.classList.remove('fa-light');
        heart.classList.add('fa-solid');
      }
   })
})
```

This is pretty straightforward as well. When the the earlier `forEach` function is employed, it iterates through all the `hearts` rendered on page and adds an event listener. This *listens* for a click on the heart and swaps in a full heart for an outlined heart--and vice-versa if you want to *unlike* something.


## Making Mistakes

There were some other functions I added to the search system that I had to remove because I didn't have time to fix the bugs. For example, my earlier code allowed a user to search by state and brewery type. Unfortunately, this code introduced some problems with the user interface, specifically the modal dialog boxes. I think one key to fixing that functionality would be to generate unique IDs for each modal and its associated brewery result on page. This will avoid the problem of having the same ID when a brewery had two or more locations (I created the IDs using the first few characters in the name of a brewery). That will have to wait for another time!