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 : 8440  
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

How to use the endsWith method in JavaScript

In this short tutorial, we are going to see what the endsWith method, introduced in JavaScript ES6, is and how it is used with strings in JavaScript. The endsWith method is…

What are javascript symbols and how can they help you?

Symbols are a new primitive value introduced by ES6. Their purpose is to provide us unique identifiers. In this article, we tell you how they work, in which way they…

Callbacks in JavaScript

Callback functions are the same old JavaScript functions. They have no special syntax, as they are simply functions that are passed as an argument to another function. The function that receives…

How to create PDF with JavaScript and jsPDF

Creating dynamic PDF files directly in the browser is possible thanks to the jsPDF JavaScript library. In the last part of this article we have prepared a practical tutorial where I…

How to make your own custom cursor for your website

When I started browsing different and original websites to learn from them, one of the first things that caught my attention was that some of them had their own cursors,…

Node.js and npm: introductory tutorial

In this tutorial we will see how to install and use both Node.js and the npm package manager. In addition, we will also create a small sample application. If you…

How to connect to MySQL with Node.js

Let's see how you can connect to a MySQL database using Node.js, the popular JavaScript runtime environment. Before we start, it is important to note that you must have Node.js installed…

JavaScript Programming Styles: Best Practices

When programming with JavaScript there are certain conventions that you should apply, especially when working in a team environment. In fact, it is common to have meetings to discuss standards…

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…

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