JavaScript Fetch API

Summary: in this tutorial, you’ll learn about the JavaScript Fetch API and how to use it to make asynchronous HTTP requests.

Introduction to JavaScript Fetch API

The Fetch API is a powerful and modern tool that simplifies making HTTP requests directly from web browsers.

If you’ve used XMLHttpRequest object before, you’ll find that the Fetch API can handle all the same tasks, but with much more elegance and ease.

Fetch API leverages Promise, providing a cleaner and more flexible way to interact with servers. It helps handle asynchronous requests and responses more intuitively.

The fetch() is a method of the global window object, which allows you to send an HTTP request to a URL with a single command. Whether you’re retrieving data, submitting a form, or interacting with APIs, Fetch API helps streamline the entire process, making your code much readable.

Sending a request

The fetch() requires only one parameter which is the URL of the resource that you want to fetch:

fetch(url);Code language: JavaScript (javascript)

The fetch() method returns a Promise so you can use the then() and catch() methods to handle it:

fetch(url)
    .then(response => {
        // handle the response
    })
    .catch(error => {
        // handle the error
    });Code language: JavaScript (javascript)

Once the request is completed, the resource becomes available, and the Promise resolves into a Response object.

The Response object serves as an API wrapper around the fetched resource.

Reading the response

If the response contains JSON data, you can use the json() method of the Response object to parse it.

The json() method returns a Promise that resolves with the full contents of the fetched resource, allowing to access the JSON data:

fetch(url)
    .then(response => response.json())
    .then(data => console.log(data));Code language: JavaScript (javascript)

In practice, you often use the async/await with the fetch() method to make the code more obvious:

const response = await fetch(url);
const data = await response.json();
console.log(data); // json dataCode language: JavaScript (javascript)

Besides the json() method, the Response object has other methods such as text(), blob(), formData() and arrayBuffer() for handling the respective data types.

Handling HTTP status code

The Response object has a status property that gives you the HTTP status code of the response:

response.statusCode language: JavaScript (javascript)

The HTTP status code allows you to determine whether the request was successful or not:

const response = await fetch(url);
if (response.status === 200) {
   // success
}Code language: JavaScript (javascript)

In practice, you’ll use a convenient property ok that checks if the status code is in the range of 200-299. If it is false, the request was not successful:

const response = await fetch(url);
if (!response.ok) {
   throw new Error(`HTTP error! Status: ${response.status}`);
}Code language: JavaScript (javascript)

JavaScript Fetch API example

We’ll make a GET request to the following API endpoint that returns a list of users:

https://jsonplaceholder.typicode.com/usersCode language: JavaScript (javascript)

Step 1. Create a new directory such as fetch to store the project files.

Step 2. Create an index.html file within the project directory:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Fetch API Demo</title>
        <script src="js/app.js" defer></script>
    </head>

    <body>
        <div id="root">
            <div id="content"></div>
            <p id="message"></p>
            <p id="loading"></p>
        </div>
    </body>

</html>Code language: HTML, XML (xml)

The HTML file has the root element that contains three elements:

  • #content for rendering the user list.
  • #message for showing any error message.
  • #loading for displaying a loading message.

Step 3. Create an app.js inside the js directory:

const getUsers = async () => {
  const url = 'https://jsonplaceholder.typicode.com/users';
  const response = await fetch(url);
  return await response.json();
};

const render = (users) => {
  return users.map(({ name, email }) => `<li>${name} (${email})</li>`).join('');
};

(async () => {
  const users = await getUsers();
  document.querySelector('#content').innerHTML = `<ul>${render(users)}</ul>`;
})();Code language: JavaScript (javascript)

How it works.

First, define a function getUsers that uses the fetch() method to fetch data from the API endpoint: https://jsonplaceholder.typicode.com/users

const getUsers = async () => {
  const url = 'https://jsonplaceholder.typicode.com/users';
  const response = await fetch(url);
  return await response.json();
};Code language: JavaScript (javascript)

Second, create a new function render that returns an HTML snippet for showing a list of users on the screen:

const render = (users) => {
  return users.map(({ name, email }) => `<li>${name} (${email})</li>`).join('');
};Code language: JavaScript (javascript)

Third, define an IIFE function that calls the getUsers() function and displays the user list on the screen:

(async () => {
  const users = await getUsers();
  document.querySelector('#content').innerHTML = `<ul>${render(users)}</ul>`;
})();Code language: JavaScript (javascript)

Handling errors

When making a web request, various errors can occur, such as network outages, server downtime, or other connection issues.

To handle the errors, you can use the try...catch statement:

(async () => {
  try {
    // fetch the users
    const users = await getUsers();

    // show the user list
    document.querySelector('#content').innerHTML = `<ul>${render(users)}</ul>`;
  } catch (err) {
    // show the error message
    document.querySelector('#message').textContent = err.message;
  }
})();Code language: JavaScript (javascript)

In this example, if an error occurs, the catch block will execute, logging the error message in the console and displaying a user-friendly error message to the user.

To test this, you can change the API endpoint to an invalid URL, such as:

https://jsonplaceholder.typicode.net/usersCode language: JavaScript (javascript)

(change .com to .net), and then open the index.html file. The page will display the following error message:

Error getting usersCode language: JavaScript (javascript)

And the console window will have the following error:

GET https://jsonplaceholder.typicode.net/users net::ERR_NAME_NOT_RESOLVEDCode language: JavaScript (javascript)

Displaying a loading indicator

If the network is slow, you will see a blank page for a brief period, which leads to a poor user experience.

To enhance this, you can display a loading message while the data is being fetched.

To do that, you can modify the IIFE function as follows:

(async () => {
  // show the loading element
  const loadingElem = document.querySelector('#loading');
  loadingElem.innerHTML = 'Loading...';
  try {
    // fetch the users
    const users = await getUsers();

    // show the user list
    document.querySelector('#content').innerHTML = `<ul>${render(users)}</ul>`;
  } catch (err) {
    // show the error message
    document.querySelector('#message').textContent = err.message;
  } finally {
    loadingElem.innerHTML = '';
  }
})();Code language: JavaScript (javascript)

Before making a request, we set the loading message to 'Loading...':

const loadingElem = document.querySelector('#loading');
loadingElem.innerHTML = 'Loading...';Code language: JavaScript (javascript)

After the request is completed, whether successful or not, you clear the loading message by setting it to blank in the finally block:

// ...
finally {
    loadingElem.innerHTML = '';
}Code language: JavaScript (javascript)

Keep in mind that if the network speed is fast enough, the loading message might not be visible. To test the loading message, you can simulate a network delay as follows:

(async () => {
  // show the loading element
  const loadingElem = document.querySelector('#loading');
  loadingElem.innerHTML = 'Loading...';

  // simulate network delay
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  await delay(2000); // delay 2 seconds

  try {
    // fetch the users
    const users = await getUsers();

    // show the user list
    document.querySelector('#content').innerHTML = `<ul>${render(users)}</ul>`;
  } catch (err) {
    // show the error message
    document.querySelector('#message').textContent = err.message;
  } finally {
    loadingElem.innerHTML = '';
  }
})();Code language: JavaScript (javascript)

In this code, we include the following snippet to introduce a 2-second delay before making the request:

// simulate network delay
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
await delay(2000); // delay 2 secondsCode language: JavaScript (javascript)

Downloading the project source code

Click here to download the project source code

Making an HTTP POST request

We show you how to make an HTTP POST request to the following API endpoint that creates a new blog post with the title, body, and userId:

<code>https://jsonplaceholder.typicode.com/posts</code>Code language: HTML, XML (xml)

Step 1. Create a new directory to store project files.

Step 2. Create a new index.html file in the project directory.

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Fetch API Demo - HTTP POST</title>
        <meta name="robots" content="noindex">
        <script src="js/app.js" defer></script>
    </head>
    <body>
    </body>

</html>Code language: HTML, XML (xml)

The index.html file includes the js/app.js file in its header.

Step 3. Create a new js/app.js file with the following code:

async function create(blogPost) {
  try {
    // Create the URL
    const url = 'https://jsonplaceholder.typicode.com/posts';

    // Create the headers
    const headers = {
      'Content-Type': 'application/json',
    };

    // Create the POST body
    const body = JSON.stringify({
      title: blogPost.title,
      body: blogPost.body,
      userId: blogPost.userId,
    });

    // Send the POST request
    const response = await fetch(url, { method: 'POST', headers, body });

    // Check the response status
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    // Parse the JSON response
    const data = await response.json();
    console.log('Success:', data);
  } catch (error) {
    // Handle any errors
    console.error('Error:', error);
  }
}

create({
  title: 'Test Post',
  body: 'This is a test post',
  userId: 1,
});Code language: JavaScript (javascript)

How it works.

First, define a function that creates a new blog post that accepts a post object:

async function create(post)Code language: JavaScript (javascript)

Second, construct a URL endpoint where the POST request will be sent:

const url = 'https://jsonplaceholder.typicode.com/posts';Code language: JavaScript (javascript)

Third, create an HTTP header to be sent with the request:

const headers = {
  'Content-Type': 'application/json',
};Code language: JavaScript (javascript)

Set the 'Content-Type' header to 'application/json' to instruct that the request’s body is in JSON format.

Fourth, create a body of the POST request by serializing the post object into a JSON string using JSON.stringify() method:

const body = JSON.stringify({
  title: post.title,
  body: post.body,
  userId: post.userId,
});Code language: JavaScript (javascript)

This assumes that the blogPost object has a title, body, and userId properties.

Fifth, send the POST request using the fetch() method:

const response = await fetch(url, {
    method: 'POST',
    headers,
    body
});Code language: JavaScript (javascript)

In this syntax:

  • url: The API endpoint where you send the POST request.
  • method: 'POST': Specifies that this is an HTTP POST request.
  • headers: Includes the header in the request.
  • body: Contains the JSON string included in the body of the HTTP POST request.

The fetch() method returns a Response object.

Sixth, check the response status and throw an error if the request was not successful:

if (!response.ok) {
  throw new Error(`HTTP error! Status: ${response.status}`);
}Code language: JavaScript (javascript)

Seventh, parse the JSON response and log the data into the console window:

const data = await response.json();
console.log('Success:', data);Code language: JavaScript (javascript)

Eighth, catch any error that occurs during the request and log it to the console:

catch (error) {
  console.error('Error:', error);
}Code language: JavaScript (javascript)

Finally, call the create() function with a blog post object containing title, body, and userId properties:

create({
  title: 'Test Post',
  body: 'This is a test post',
  userId: 1,
});Code language: JavaScript (javascript)

Step 4. Open the index.html in the web browser. It’ll execute the app.js file that makes an HTTP POST request.

Step 5. Open the console window, you’ll see the following message if the request was successful:

javascript fetch api post method

Downloading the project source code

Click here to download the project source code

Summary

  • Fetch API provides a simpler and more flexible way to make HTTP requests compared to XMLHttpRequest object.
  • Use fetch() method to make an asynchronous web request to a URL.
  • The fetch() returns a Promise that resolves into a Response object.
  • Use the status or ok property of the Response object to check whether the request was successful.
  • Use the json() method of the Response object to parse the contents into JSON data.
  • Use the try...catch statement to handle errors when making HTTP requests.
Was this tutorial helpful ?