Hashing it out, API-style

Steph O
6 min readMar 4, 2021

Working with APIs

The possibilities of working with data in order to achieve something are endless. As programmers (myself, a coder-to-be), working with data is (will be) our bread and butter. One of the many ways in which complex data is made available to us is the API.

Short for “application programming interface,” API is basically a gift-giving service with gift-wrapping included (either at a cost or free of charge), neatly packaging the data we need. Typically, this data can only be provided by some entity, like a company or organization, that is privy or has special access to pockets of data. The entity avails its collected data and/or functionality of its data to us. We, then, do something with it to achieve an outcome for our intents and purposes.

My Cocktail CLI application

For my own Ruby application, TheCocktailDB provides the data I needed to code a way for a user to learn about any cocktail that TheCocktailDB knows about, as indicated by a cocktail’s inclusion within its database.

Using its provided API URL link, my Cocktail CLI application draws on the database to store specific sets of information and reveal only what’s sought after by a user. Other application details and mechanics aside, tapping into the public database is achieved in class Api, whose sole purpose is to interact with the database.

Based on a user’s keyword submission prompted by a different method, this method’s accepted argument of “user_input” will adjust accordingly; and the URL, contingent to user activity, is stored in the local variable “url” to make it easier for us to work with. To complete “url”’s ability to be functional here, it’s included within the context of class method #get_cocktails_by_name:

Now that we have the link to access the database of alcoholic libations, we need to:

  1. Request the API for user-specified data from the database
  2. Receive the data
  3. Save it in our own database: This is important to do so that we’re able to make some sense of the data and be able to manipulate it in a way that provides us with the most value of use/functionality (parsing the data), while minimizing the lift it takes to interact with such a complex, external database via API.

The line of code that begins with local variable “response” achieves steps 1 + 2:

Step 3? Now that we’re at the point where we’ve got data back to us after submitting search keyword(s) for cocktails, we have to save it. But how…

Data looks hairy to me

Let’s say “user_input” = “vodka”… Variable “url” will adjust accordingly and get back to us the data that correlates with keyword “vodka.” Using the Pry gem via code “binding.pry,” we can see that “input” does indeed = “vodka”… “url” adjusted accordingly… and “response” returns to us a hash of data with a key called “drinks” and a value of an array of hashes of attributes that describe the cocktail “Long vodka” (seen below)… followed by more hashes of other cocktails in the same format (not seen).

Clearly, the data that API returns to us came back uber complicated-looking, which will be cumbersome to work with as is. For us to save this data as is means more work on our end to extract the pieces of information we need and for it to eventually be formatted/presented in a cleaner, more intuitive way.

So, again, how do we save, or parse, the data in a way that’s most meaningful for us to use? We’ve got to create new objects – one per cocktail from the list of many cocktails that’s returned to us. In other words, we need to instantiate new instances of our pre-defined Cocktail class by iterating over the returned hash.

Error: Where it gets hairier

Where local variable “cocktail” should create a new instance per iteration of the hash stored in variable “response,” running the application and entering “cocktail” in pry returns a “NameError”…

This tells us that something is wrong with the way we’re 1) iterating over the hash stored in “response” and/or 2) instantiating new objects. After an inordinate amount of time spent toward trial-and-error, here’s my lesson of the day: be clear on what exactly it is you’re iterating over.

What kind of object(s) are you calling an iterator method on?

Referring back to what variable “response” returns, “response” stores a nested hash. In order to access the values in a nested hash, it’s convention to tack on the keys wrapped in brackets, until the last key points to the value we seek to access.

In our application, we’re getting trouble because we’re calling the #each method on a nested hash — a HASH of an ARRAY of more HASHES.

In other words, we’re iterating over a hash that has ONE array. I need to access each cocktail hash, which is nested in an array, which is nested further in a hash.

Top level: 1 Hash, {drinks}

2nd level: 1 Array, [value] — (lists all associated cocktails)

3rd level deep: 1 Hash, {hash} — (lists all attributes per cocktail)

Essentially, by calling #each on “response,” we’re attempting to jump over the 2nd level to iterate, hoping to pass in the values found at the 3rd level.

Solution: Save the data in our own database

To access the values we need in our nested hash — which is stored in variable “response,” — we have to keep iterating through each level of data, until we’ve reached the dataset we need for instantiation, or creating new objects.

  1. Call #each on the top level hash, “drinks”
  2. Call #each on the 2nd level array, “value”
  3. Call #new on the Cocktail class to instantiate a new Cocktail object (instance); passing the dataset of attributes of a single cocktail (a single element of the “value” array) that’s stored in “hash” as the argument upon instantiation (accomplished via class Cocktail’s #initialize method).

Even better: Cleaning it up

To optimize our setup, we can manipulate how we opt to store the data in our variable “response” by requesting via API for the data to return back to us starting from the next level deeper directly.

This is indicated by appending the top level hash key, [“drinks”], to our call for data; which receives the data starting from level 2, the value that hash key “drinks” points to — the array of hashes.

Next, we’re able to iterate over that array, passing in each cocktail hash as the exact argument upon calling our #initialize method for new Cocktail objects in order to assign and save its attributes in our own database through an automatic and predetermined way.

--

--