A FULFILLED PROMISE - Using the FETCH API to make AJAX calls

Making partial web page updates by using fulfilled promises with fetch

by Iveta Karailievova Date: 26-12-2020 JavaScript fetch promise AJAX

In this article we talked about what AJAX calls are and how to use them in a traditional way, by using the XMLHttpRequest (XHR) object. In short, thanks to AJAX calls a web page can be partially updated without having to reload the whole content. This leads to better loading speeds, usability and nowadays is an indispensable part of modern web development. Now we will focus on a more modern approach, which is by using the fetch Web API. 

In this article, as an example, we will build a functionality which generates and diplays on our page random Rick and Morty characters. The only language we need to use is plain (vanilla) JavaScript, and the rest of the magic (the back end part) will be provided by the Rick and Morty REST API

Web APIs - what are they?

A Web API is an application programming interface for the Web. To put it simply, when you write code for a web page, you may use a web API, which makes your job way easier, for example, by providing the web browser with more the functionality, by simplifying complex functions or by simplifying the code’s syntax. One of those useful APIs is fetch, which helps us to make web requests in a more intuitive and simpler way.

What is the fetch API and why is it better than XHR?

It is a relatively new, native JavaScript API. Fetch API was introduced with ECMAScript 6, also known as ES6 and ECMAScript 2015, which was the second major revision to JavaScript. 

Fetch allows you to make asynchronous network requests to web servers similar to the XMLHttpRequest, but with the advantage of being more elegant, simpler and cleaner. The main difference between XHR and FECTH API is that the Fetch API uses Promises. Thanks to this, you will not have to deal with callback functions and the callback hell they could potentially result in. Also, with XMLHttpRequest, the syntax you need to use in  your code is more complex and thus might lead to errors. We can without doubt say that fetch API is an improvement over the traditional XMLHttpRequest method, and it offers us more features.

Although it is supported by most browsers today, it is not supported by Internet Explorer. So if you need your code to be compatible with IE, you should consider using polyfills. These are basically pieces of JavaScript code responsible for providing modern functionality on older browsers that do not natively support it.

How to use fetch – an example where we retrieve data from an API

As its name suggests, the fetch API provides an interface for fetching, obtaining resources. In order to call the fetch method, we need to supply one argument – the URL or path to the resource from which we want to get our response. 

Let’s get practical and start setting up our example. Here is the HTML markup:

in our index.html file, we put inside the <body> element:

<label for="number">Choose a number</label><br>
<input id="number" type="number" min="1" max="671" />
<img id="avatar" src="" alt="" />

and to make it a little more appealing, some CSS:

body {
background-color: coral;
color: grey;
font-size: xx-large;
text-transform: uppercase;
text-align: center;
font-family: Verdana, Geneva, Tahoma, sans-serif;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;

Next part - our vanilla JavaScript code with the fetch method

Let’s start with the easy part – creating constants to access the DOM elements which we are going to need:

// the inputValue allows us to access the value of the user's input, i.e. the chosen number
const inputValue = document.getElementById('number')
// this constant allows us to set the image to be displayed, by dynamically changing its "src" attribute
const avatar = document.getElementById('avatar') 

Since we want to be able to access different characters, so we keep the id part flexible – by using the replace() method. We will be supplying the "id" number dynamically, when the user changes the value of the input form.

const API_URL = 'https://rickandmortyapi.com/api/character/:id'
const URL = API_URL.replace(':id', id)

And now the interesting part – the fetch method returns a promise.

What is a JavaScript promise? 

Promises are a modern approach, a solution to the problems callback functions brought upon us. A promise is a placeholder for a value it will hold in the future, when the asynchronous request is resolved. To this promise, we can attach handlers, so that certain action is carried out in the case the promise is fulfilled and another if the promise is rejected. 

You could also think of a promise being in some ways similar to an event, but with the difference that a promise can only succeed or fail once, and that the result is final, meaning it cannot switch from being fulfilled to being rejected or vice-versa. 

In our case, the promise will at some point in the future contain the response, more precisely a Response object, a HTTP response. But careful – in order to be able to work with the actual data we received from the API, we first need to extract the response’s body, by using the json() method. This way, we parse the response into JSON. But, and this might seem somewhat confusing, the json() method returns another promise. This is because the reading of the stream containing the response is happening asynchronously.

We work with promises by assigning handlers to them, namely the then() and catch() method.

Promises can have one of the following states:

  • pending – this state is assigned automatically after the promise is created, it means so far the promise was neither fulfilled nor rejected;

  • fulfilled – the promise has been completed with success and the result we obtain is the value of the operation;

  • rejected – the promise has not been completed successfully, and the result we obtain is the reason why this happened (the error);

Handling errors when using fetch

in the case of fetch method, the fetch promises are rejected only when a network error is encountered (such as permissions issues, misconfigured CORS on the server-side, DNS lookup failure, etc.) HTTP responses with codes 4xx and 5xx are not network errors, so the catch() attached to the promise will not be fired in the case of these types of errors. That is why we need to check the fetch response’s ok property, like this:


it returns a Boolean value, being true if the response returned successfully.

By adding the following error-handling function, we can make sure that our errors are thrown and catched correctly, like in the case that the user chooses an id which is out of the APIs range of characters:

function handleErrors (response) {  
    if (response.ok) {
       return response
     throw new Error(response.statusText) 

So, now we have to chain all the promise then() methods, like this:

function getCharacter (id) {
  const URL = API_URL.replace(':id', id)
  // first we attach the error handler function

// next, if there was no error, we use the json method on our response object
    .then(function (response) {
      return response.json()

    // since the response.json() method used on a Response object returns another promise, we attach another then handler on the data we receive

    .then(function (data) {
      const newCharacterJSON = data
      // upon studying the response data structure, we know we need to access the "image" JSON key to get the image URL
      var imagePath = newCharacterJSON.image

      // now we set the src attribute of our image element to the URL from the reponse
      avatar.setAttribute('src', imagePath)
    // this part of the code allows us to correctly catch the errors which might have appeared, both errors of the original promise being rejected as well as the errors which are thrown by our handleErrors function
    .catch(function (err) {
      console.log('Something went wrong!', err)

Note: the response you get is not JSON, but an object with a series of methods that can be used depending on what you want to do with the information. Since the Rick and Morty API we’re using will return JSON to us, the returned response will have .json() method. We just need to call .json() on the response variable.

And finally, we need to think of a way of calling the function. We do this by adding an event listener to our input element. In this example we chose to use the change event, which gets triggered every time the value of the input field gets changed.

inputValue.addEventListener('change', function () {
const choosenNumber = inputValue.value

To take a better look on this solution, here you can see the full code and a working demonstration.


Even though the results we are getting might seem the same, using fetch API over XMLHttpRequest seems to be the correct way to write our code in the future, because it allows us to handle responses in a more organized way and spares us the trouble of descending into our little callback hell.

by Iveta Karailievova Date: 26-12-2020 JavaScript fetch promise AJAX hits : 7139  
Iveta Karailievova

Iveta Karailievova

Originally coming from a marketing background, decided to turn her life around and immerse herself into the wonderful exciting and most importantly – never boring world of technology and web development. Proud employee at MA-NO . Easily loses track of time when enjoying working on code. Big fan of Placebo, cats and pizza.


Related Posts

Difference between arrow and normal functions in JavaScript

In this tutorial we are going to see how arrow functions differ from normal JavaScript functions. We will also see when you should use one and when you should use…

JavaScript Arrow functions: What they are and how to use them

In this article we are going to see what they are and how to use JavaScript Arrow Functions, a new feature introduced with the ES6 standard (ECMAScript 6). What are Arrow…

How to insert an element into an array with JavaScript

In this brief tutorial you will learn how to insert one or more elements into an array with JavaScript. For this we will use the splice function. The splice function will not…

What is the difference between primitives types and objects in JavaScript?

In this short tutorial we are going to look at the differences between primitive types and objects in JavaScript. To start with, we're going to look at what primitive types…

How to get DOM elements with JavaScript

When you access any element of the DOM, it is usual to save it in a variable. This is something that at first might seem very simple, but if you…

How to reverse an array in JavaScript

In this tutorial we are going to see how you can change the order of the elements of an array so that they are inverted. You could use a loop…

How synchronize the scroll of two divs with JavaScript

In case you have two divs of different sizes you may sometimes want to scroll both at the same time but at different speeds depending on their size. For example,…

How to use the codePointAt method in JavaScript

The JavaScript codePointAt method has more or less the same function as the charCodeAt method, used to get the 16-bit Unicode representation of the character at a certain position in…

How to check if a value is a number in JavaScript

In this short tutorial we are going to look at the various methods that exist to find out if a value is a number in JavaScript.   1. Using the isNaN() function   One…

How to use the charCodeAt method in JavaScript

The charCodeAt method is accepted by strings in JavaScript, returning the 16-bit Unicode code of the character at the position we pass as a parameter to the method. The charCodeAt method…

How to use the charAt method in JavaScript

The charAt method is accepted by strings in JavaScript, returning the position of the character passed as a parameter within the string. If the string contains multiple occurrences of the character…

Strings in JavaScript: What they are and how to use them

In this tutorial we are going to explain what strings are and how they are used in JavaScript. The tutorial is intended for people who are learning to program in…

We use our own and third-party cookies to improve our services, compile statistical information and analyze your browsing habits. This allows us to personalize the content we offer and to show you advertisements related to your preferences. By clicking "Accept all" you agree to the storage of cookies on your device to improve website navigation, analyse traffic and assist our marketing activities. You can also select "System Cookies Only" to accept only the cookies required for the website to function, or you can select the cookies you wish to activate by clicking on "settings".

Accept All Only sistem cookies Configuration