3 Ways to Fetch Data from an API Endpoint in JavaScript

Aditya Nautiyal
6 min readSep 19, 2023
Photo by Joan Gamell on Unsplash

Making static HTML pages with some styling is fun. To take it a step further and add dynamic data to your web page, you need to get data from external sources. This happens when websites communicate with each other.

Before that, let’s understand how websites work.

When one piece of code (eg. your web page) communicates with another piece of code (eg. a web server), they do so with something called an API (Application Programming Interface). In the context of the web, these interfaces are called Web APIs.

A Web API is nothing but a URI (Uniform Resource Identifier) like the one we use to access websites on the internet. For example: google.com. A URI points to a resource/data on the web. In the case of google.com, it points to Google’s home page. When a user hits google.com in their browser, it sends an HTTP request with the GET method to Google’s server. The server responds to the request with a web page that is displayed on the screen.

Websites communicate all the time. Sometimes with other websites to share data and sometimes to their own database server to persist information. This communication is done with a common protocol that is understandable by all the web servers on the internet. This protocol is HTTP, a common language of the web. The device that sends the HTTP request is called the HTTP client and the one that receives them is called the HTTP server. For every request that the server receives, it must send a response to it, be it the requested resource or an error.

You can read more about HTTP here.

A method in an HTTP request indicates the type of action the client wants to perform on a resource. The most common ones are:

  • GET eg. get the data/web page
  • POST eg. upload an image on the server
  • PUT eg. change/update username
  • DELETE eg. delete user’s account

Your browser sends HTTP requests all the time to communicate with the web. It also provides some in-built methods/APIs that websites can use for communication over the internet.

The most popular ways to make HTTP requests from your web page:

  1. XHR or XMLHttpRequests
  2. Fetch API
  3. Axios

Let’s Write Some Code

First, open VS Code and create a new folder called data-fetching. Create the following files inside this folder :

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Website</title>
<link rel="stylesheet" href="./styles.css" />
<script src="./index.js" defer></script>
</head>
<body>
<h1>Data Fetching in JavaScript</h1>
<button id="getButton">GET Data</button>
<button id="postButton">POST Data</button>
<div id="dataDiv"></div>
</body>
</html>

styles.css

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
width: 100vw;
min-height: 60vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 1rem;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
button {
background-color: #1f618d;
border: none;
border-radius: 5px;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
font-size: 16px;
}

#dataDiv {
line-height: 1.5;
width: content;
background-color: #f2f2f2;
text-align: center;
}

index.js

// adding event listeners to the buttons

getButton.addEventListener("click", () => {
console.log("Get Button clicked")
});

postButton.addEventListener("click", () => {
console.log("Post Button clicked")
});

When index.html is opened inside the browser (Live Server extension on VS Code), you should see a similar web page:

Web page created with the provided code
Basic web page

1. XHR (XMLHttpRequest)

XHR (XMLHttpRequest) objects can be used to interact with servers. It is one of the earliest methods to communicate with servers. Therefore, it is supported by most modern browsers.

To send HTTP requests, create a function named sendHttpRequest in your index.js file.

function sendHttpRequest(method, url, data) {
return new Promise((resolve, reject) => {
// create an xhr object
const xhr = new XMLHttpRequest();
// set http method and url of the API
xhr.open(method, url);
// set the metadata
xhr.responseType = "json";
xhr.setRequestHeader("Content-Type", "application/json");
// callback function when response is recieved
xhr.onload = () => {
if (xhr.status >= 400) reject(xhr.response);
else resolve(xhr.response);
};
// callback function when error is recieved
xhr.onerror = () => {
reject("Something went wrong!");
};
// send the http request
xhr.send(JSON.stringify(data));
});
};

As you might notice, XHR does not have an in-built support for JavaScript Promises.

Now, make changes to the buttons’ event listeners:

getButton.addEventListener("click", () => {
// call the sendHttpRequest function which returns a promise
sendHttpRequest("GET", "https://reqres.in/api/users")
.then((responseData) => {
// insert the data in dataDiv
dataDiv.textContent = JSON.stringify(responseData);
console.log(responseData);
})
.catch((error) => {
dataDiv.textContent = JSON.stringify(error);
console.error(error);
});
});

postButton.addEventListener("click", () => {
// POST method to register a user
sendHttpRequest("POST", "https://reqres.in/api/register", {
email: "eve.holt@reqres.in",
password: "pistol",
})
.then((responseData) => {
dataDiv.textContent = JSON.stringify(responseData);
console.log(responseData);
})
.catch((error) => {
dataDiv.textContent = JSON.stringify(error);
console.error(error);
});
});

Here, we are making HTTP requests to reqres.in which is a hosted, public dummy API for testing purposes.

When the “GET Data” button is clicked

You may use this data however you require.

Pros:

  • Broad browser support
  • Fine-grained control

Cons:

  • Complex API
  • Callback-based, can lead to callback hell
  • No native support for Promises

2. Fetch API

Modern browsers also provide a fetch() function for data fetching. It is a modern alternative to XHR which is very verbose and complex.

To use fetch(), make the following changes to your sendHttpRequest function.

const sendHttpRequest = (method, url, data) => {
// fetch() returns a Promise object
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
}).then((response) => {
console.log(response); // response is stream data
// Handle HTTP errors
if (response.status >= 400) {
// convert stream data to JSON
return response.json().then((errorResponseData) => {
const error = new Error();
error.message = "Something went wrong!";
error.data = errorResponseData;
throw error;
});
}
return response.json();
});
};

This is much simpler and straightforward as compared to XHR.

Note: response from fetch is streamed data (ReadableStream type) form by default. To get JSON data, use response.json()

Pros:

  • Simpler syntax
  • Native promise support
  • Modern features like streaming responses
  • Can handle JSON, FormData, Blobs

Cons:

  • No backward compatibility
  • Limited error handling
  • Rejects promise for network errors and not for HTTP errors
  • Lack of progress events like in XMLHttpRequest (eg. tracking the progress of upload and download)

3. Axios

Axios is a popular HTTP client that can be used with both browsers and with Nodejs. On the browser, it uses XMLHttpRequests and therefore has equally broad browser support. On the server, it makes use of the default http package from Nodejs.

To use Axios, we need to install it in our project since it is not available in the browser by default like fetch and XHR. Here we can use Axios from a CDN.

Add the following line in the head tag of your index.html file:

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

This adds Axios to our project. Now we can use it in our index.js file as follows:

getButton.addEventListener("click", () => {
axios
.get("https://reqres.in/api/users?page=2")
.then((response) => {
dataDiv.textContent = JSON.stringify(response.data);
console.log(response.data);
})
.catch((error) => {
dataDiv.textContent = JSON.stringify(error);
console.error(error);
});
});

postButton.addEventListener("click", () => {
axios
.post("https://reqres.in/api/register", {
email: "eve.holt@reqres.in",
// password: "pistol",
})
.then((response) => {
dataDiv.textContent = JSON.stringify(response.data);
console.log(response.data);
})
.catch((error) => {
dataDiv.textContent = JSON.stringify(error);
console.error(error);
});
});

The code is much shorter now.

Pros

  • Simple syntax and easier to use than XMLHttpRequest and fetch API
  • Has good browser support since it uses XMLHttpRequest
  • Automatic request and response transformation
  • Has support for interceptors and middleware
  • Built-in error handling

Cons

  • Additional dependency
  • Larger bundle size than the other two
  • Less control over low-level detail
  • Customization limitations unlike low-level APIs like fetch and XMLHttpRequest

Conclusion

These were the 3 most popular ways to fetch data from an API endpoint in JavaScript. You might not use XMLHttpRequest often but it is used by many libraries because of its fine-grain control and browser support. Axios is commonly used in larger projects because of its simpler API and browser support, thanks to XMLHttpRequest that is used behind the hood.

Further reading:

--

--