Fetch Requests & AJAX

Kesliebox
5 min readMay 10, 2021
Photo by K. Mitch Hodge on Unsplash

I’ve just wrapped up the fourth phase of the Flatiron software engineering boot camp, which required creating a SPA(Single Page Application) with JS on the frontend and Ruby on Rails as the backend API. As with any web-based application, this required making lots of server requests to get data from the backend and render it for the user on the frontend. In order to prevent any page refreshes, we were required to use the Fetch API to make fetch() requests. It took me a minute to get the hang of what was going on behind the scenes, so I’ll try to break this down a bit to hopefully make it easier on you, dear reader.

Here’s a basic fetch() request (nested within another method that I invoke as a callback method elsewhere in the application):

In this snippet, we fetch(herbsURL), meaning, we make an asynchronous HTTP request that employs a combination of Promises, XMLHttpRequestObjects, JSON, asynchronous Input/ Output, the event loop.

Playing fetch with a dog is a truly great analogy…

  1. You throw the ball (make a fetch request to the…dog, ahem I mean server)
  2. The dog (hopefully) fetches the ball (the server hopefully fetches the resource, ie URL route, that is passed as a parameter).
  3. The dog brings the ball back to you (the server delivers you access to the resource you requested ).
  4. The dog then does something else, ie prepares for the next toss, runs around, etc (the server .then makes a promise to try to render a JSON object for you to then use .json() to convert the resolved “response” of the Promise into a JS object).
  5. The dog then does another thing (the server .then makes another Promise to try to do what you tell it to do next to the resolution of the previous promise).

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. — https://developer.mozilla.org

Promises perform asynchronous tasks and basically resolve in what we call a response. We don’t know when this resolution of a promise will occur or if it will even result in a fulfillment of our request — sometimes the response is a status of pending, or even rejected if the request failed all together, hence the asynchron-icity. This could result from a bad resource, a typo, a server not being run (ie in the case of a Rails local server as was used in my project), etc . The point is, if the promise doesn’t resolve into fulfilled or rejected, the next line of code will not run because it relies on the resolution of that promise not just the initialization of it. This basically means that the code we see in the parenthesis following .then()is a callback function that will only be executed if the promise is resolved.

So to recap, we make a fetch() request for the resource of our choosing. This returns a promise, which resolves to the response sent back from the server. We call .then() on this resolved promise to access the response (which is automatically passed into the callback function as a parameter ) and request a new promise by calling .json() on the response to create a JS object, on which we request another promise from which the response is again automatically passed in as the argument to the function inside the next .then() method, granted all goes well and the promise is fulfilled, we can perform some other bit of code on that response to manipulate the DOM, etc and if it doesn’t, we can .catch() the errors to help us figure out what to do next.

In the above snippet, we see how fetch() can be used to make other CRUD related requests to the server as well. This looks like a lot more code than before, but in reality, it is minimal.

All we are doing differently is adding in an object, what I’ve called options, which includes a key of method: and a value of "POST" to make a post request to the 'http://localhost:3000/herbs' resource, saved in a variable called herbsURL. Then we have a key of headers: with a value of an object: {“Content-Type”: “application/json”, “Accept”: “application/json”} with key/value pairs representing the type of content we are sending and the type of content we are accepting. Lastly, we have to send the content of the body: with key/value pairs that represent the attributes that we have set up on our backend with values entered by the user on the frontend. In order to turn it into a JSON string format that the server can understand, we use JSON.stringify() and pass in a parameter of our body's respective key/value pairs.

We send this big Object with the method: , headers: , body: as part of our fetch() request to the server, asking it to post the information to the database, creating a new record. Upon completion of our request, we go through all of the aforementioned steps of the fetch() request, waiting for the resolved promise’s response, calling .then() and so on, and in this case, instantiating a new instance of the class on our frontend as well, so as to make sure our frontend data matches our backend. The last bit of code is simply to error handle and prevent bad data; it is not part of the fundamental parts of the fetch() request.

In my application, I exclusively invoke fetch through other callback methods in order to further create an asynchronous application and base the code on the user directed experience. Using fetch() in conjunction with other asynchronous code, ie .addEventListener() or some other code with fetch() being invoked through a callback method can be really helpful in not overloading the page load with server requests and only fetching data from the database or manipulating the DOM in ways that are necessary to the user experience as deemed by the user’s interaction with the application. It makes it so that the user creates their own experience as they wish to do, instead of making the server do all the work up front before we even know what the user will need. It means less wait time for the user and less memory usage for the server/ browser, win win for everyone! Thanks AJAX!

--

--