How I Learned To Stop Worrying and Embrace Fetch()

R. Cory Stine
8 min readOct 26, 2021

--

I’ve been on my coding journey for more than a year now and I’m not sure there’s any language I’ve been more excited to learn than JavaScript. As a former animator, it was a thrilling idea to add new life and motion to my web applications.

But as with any new technology, there were definitely road bumps on my path to understanding. I found it fairly easy to grasp DOM manipulation — especially Event Listeners — but as the curriculum moved into asynchronous JavaScript, and fetch() in particular, I felt increasingly lost. The concepts were becoming more and more abstracted and I didn’t have a tremendous amount of practical examples to emulate or learn from.

If I’ve discovered anything from my time at Flatiron, it is that the practical knowledge often comes when you need it most — when you are working on your project. And after building out a functioning application using a JavaScript/HTML/CSS frontend and a Rails backend, I feel like my understanding has grown ten-fold.

Hopefully, if you’re in the same situation, this will grease the wheels.

So why do we need to use fetch() to begin with?

The fetch() method is one of the most popular ways to implement AJAX (or “asynchronous JavaScript and XML”), a technique to make requests to a server and update the DOM of your web app. Most importantly, AJAX lets us update a page without reloading it, read data from a server after that page has been loaded, and send data to a server in the background.

AJAX solves a critical problem: we need to keep the attention of the people who visit our site. That often means lots of data and media. But it also means loading things quickly enough that they won’t drift somewhere else. With AJAX, we render our HTML and CSS first, and only then does our JavaScript begin adding things to the DOM. It is extremely useful for building a sleek, modern website; especially because of that first feature.

Note: While AJAX originally sent information between the server and the browser encoded with XML (as the name would imply), more modern apps tend to use JSON — a String that JavaScript knows how to turn into an Object. We can use the returned JSON data to update our DOM.

Which brings us back around to the dreaded fetch().

Let’s break it down simply: fetch() gets data for us. Either from an external API or one we build for ourselves. That’s it.

In my project, FanMade, I wanted to build a curated collection of custom crafts and products made by fans of pop culture properties like Star Wars, Marvel and Dungeons and Dragons. There are SOOOO many shops on services like Etsy and Redbubble that sell amazing merchandise, but because of the sheer volume of content, a lot gets buried in the mess. FanMade would help to highlight the very best of what is out there.

To build out this concept, I first constructed a simple API in Ruby that would provide data about interesting fan-made products, the categories they fit into, as well as the fandoms they represent. But to use this data on my frontend, with JavaScript, I would need to retrieve it. And how do we get data? That’s where fetch() comes in.

const endPoint = "http://localhost:3000/api/products"function fetchProducts(){
fetch(endPoint)
}

To start off, I created a global variable called endPoint that I would use to reference the URL for my products API. Next, I set up a function called fetchProducts() that would call fetch() and pass in the endPoint link.

Now I’ve got the data about my products, but haven’t actually done anything with it yet. What happens next? Or rather, what happens then?

The then() method was very difficult for me to wrap my head around at first. It felt so nebulous and abstract. But in truth, then() is just the method we implement to use the data we gathered with fetch(). It takes in a function to perform a specific task with that data. It’s also chainable, meaning you can run a function, then run another, then run another (and so on and so on) until you’ve completed the task you were aiming for.

Often, after fetch(), you’ll want to take the response you receive from your API and turn it into JSON, so that the data is easier to work with.

const endPoint = "http://localhost:3000/api/products"function fetchProducts(){
fetch(endPoint)
.then(response => response.json())
}

Just to clarify, “response” in this case would be the data returned from my endPoint. The arrow expression creates a compact function, allowing me to call .json() on the response data, returning it as a JSON Object. We aren’t simply pulling the response out of thin air.

Even at this point though, we haven’t really done much with our data. We have it a new format, but it hasn’t been operated on otherwise. This is where then()’s chainable nature comes in handy — because we can keep this party going.

function fetchProducts() {
fetch(endPoint)
.then(response => response.json())
.then(products => {
products.data.forEach(product => {
let newProduct = new Product(product, product.attributes);
document.querySelector('.pc-row').innerHTML +=
newProduct.renderProduct();
})
})
}

We can run a function on that new JSON Object. In this case, my goal was to render a product card for each of the products in the JSON Object, letting the user see everything that we have curated for them.

I started by passing the JSON Object into .then() and calling it products. Next, I looped through products.data, which provided me with the Array of products inside of the Object, pulling out each individually to run a function on it.

Separately I had created a JavaScript class called Product that used the special method constructor to build and initialize Product objects, then push them into an Array called Product.all. It also featured a class method named renderProduct() that would return the HTML necessary to create a card containing that Product on the site.

class Product {
constructor(product, productAttrs){
this.id = product.id;
this.name = productAttrs.name;
this.company = productAttrs.company;
this.fandom = productAttrs.fandom;
this.price = productAttrs.price;
this.description = productAttrs.description;
this.link = productAttrs.link;
this.img = productAttrs.img;
this.category = productAttrs.category
Product.all.push(this);
}
renderProduct() {
return `
<div class="card" data-id= ${this.id}>
<img src=${this.img} width="300" height="300">
<a href=${this.link}><h3 class="card-title">${this.name}
(${this.category.name})</h3></a>
<h4 class="price">$${this.price}</h4>
<p>${this.company} - ${this.fandom.name}</p>
<p>${this.description}</p>
<button class="delete-button" type="delete"
name="delete" value="Delete">Delete</button>
</div>`;
}
}
Product.all = []

So as I loop through products.data, I call new Product to create a new Product object for each element of that array. I pass in product, as well as product.attributes so that my constructor can build the new object using the data I’ve received from my API. I assign the resulting object to a variable called newProduct.

Since I finally have assigned the incoming data to an object, I can use that information to render a product card. First, I grab the container object for my cards from my HTML using document.querySelector(“.pc-row”), chaining the method .innerHTML to signal that I want to manipulate what goes between those div tags. I used the operator += to indicate that I want to add what follows, then call newProduct.renderProduct() to render the object I created previously.

The result looks something like this:

Every object from my API is displayed for the user to see, showing the connection between the backend and the frontend! This is a great example of the power of fetch() and shows how simple it is when you break it down into parts.

  1. We use fetch() to get the raw data from the API.
  2. .then(), we take that data response and turn it into a JSON Object that is easier to work with.
  3. .then(), we loop through the JSON Object to create new Products and render their product card on the page.

In my opinion, fetch() is much more digestible in action than it is purely as a concept. And this is certainly not the only way to use it. For example, I also used fetch() in my postProduct function to create a new product on the site through a form.

function postProduct(name, company, fandom, price, desc, link, img, category){
fetch(endPoint, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
name: name,
company: company,
fandom_id: fandom,
price: price,
description: desc,
link: link,
img: img,
category_id: category
})
})
.then(response => response.json())
.then(productResp => {
const product = productResp.data;
let newProduct = new Product(product, product.attributes);
document.querySelector('.pc-row').innerHTML +=
newProduct.renderProduct();
resetForm();
})
}

Let’s do one last breakdown to hammer this home.

  1. We create a function called postProduct and pass in all of the elements necessary to create a Product in our database.
  2. We use fetch() to get the data from the endPoint API URL (the same one we’ve used before). In this case, we also include our HTTP Verb (“POST”), our headers (where we indicate that the format we’ll be using is JSON), and the data we want to send, which we assign using the passed in elements and turn into a string using .stringify(). This will all assist with persisting incoming form data to our database.
  3. .then() we take the response from our fetch and turn it into a JSON object.
  4. Since we will only receive a single element from our form, we don’t need to iterate as we did with our previous function. Instead, we use the JSON object we’ve received to create a new individual Product and render its card inside the correct container div. This code should look very familiar.
  5. Finally, I call my custom function resetForm() to delete all of the form data on submit, clearing it for future use.

There are some obvious differences here, specifically with how we work with POST instead of GET; but largely it is the same process of retrieving information from our API and working with it to post and render a new product.

Understanding fetch() is crucial to understanding JavaScript as a whole. It helps us to build a bridge between our backend and frontend, opening up so many new possibilities. While it can be a challenge to wrap your head around at first, it is simpler in execution than it might look.

--

--

No responses yet