Master of the universe

Introduction

In modern web applications, making HTTP requests to communicate with servers is essential for fetching and updating data. XMLHttpRequest and Fetch API are the two primary ways to make HTTP requests in JavaScript. This guide will cover the basics of implementing both XMLHttpRequest and Fetch API, along with advanced usage, best practices, and error handling.

Understanding XMLHttpRequest

What is XMLHttpRequest?

XMLHttpRequest is a JavaScript object that allows web applications to communicate with servers via HTTP requests. It was introduced by Microsoft in the early 2000s and later became a standard for all major web browsers. XMLHttpRequest enables developers to fetch data from servers and update the content of web pages without reloading them, a technique known as AJAX (Asynchronous JavaScript and XML).

Creating an XMLHttpRequest Object

To create an XMLHttpRequest object, use the following syntax:

const xhr = new XMLHttpRequest();

Once you have created an XMLHttpRequest object, you can use the open() method to initialize a request. The open() method accepts three arguments: the request method (e.g., "GET", "POST"), the URL, and a boolean value that specifies whether the request should be asynchronous or not.

xhr.open("GET", "<https://api.example.com/data>", true);

Handling Responses

To handle the response from an XMLHttpRequest, you can use the onreadystatechange event listener:

xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};

The readyState property represents the state of the request, while the status property represents the HTTP status code of the response. When readyState is 4 (request finished and response is ready), and status is 200 (OK), you can process the response data.

Making GET Requests

Here's an example of making a GET request with XMLHttpRequest:

const xhr = new XMLHttpRequest();
xhr.open("GET", "<https://api.example.com/data>", true);

xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    const data = JSON.parse(xhr.responseText);
    console.log(data);
  }
};

xhr.send();

In this example, we send a GET request to fetch data from a server and parse the JSON data from the response.

Making POST Requests

To make a POST request with XMLHttpRequest, you need to change the request method and send data in the request body:

const xhr = new XMLHttpRequest();
xhr.open("POST", "<https://api.example.com/data>", true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 201) {
    console.log("Data created successfully");
  }
};

const data = { key: "value" };
xhr.send(JSON.stringify(data));

In this example, we send a POST request to create new data on the server. We set the request header "Content-Type" to "application/json" to indicate that we are sending JSON data.

Error Handling

To handle network errors with XMLHttpRequest, you can use the onerror event listener:

xhr.onerror = function () {
  console.error("Request failed");
};

To handle HTTP errors, you can check the status property in the onreadystatechange event listener:

xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(xhr.responseText);
    } else {
      console.error("Error:", xhr.status, xhr.statusText);
    }
  }
};

Understanding Fetch API

What is Fetch API?

Fetch API is a modern approach to making HTTP requests in JavaScript. It was introduced in 2015 as a more powerful and flexible alternative to XMLHttpRequest. Fetch API is based on Promises, which makes it easier to write and maintain asynchronous code.

https://www.youtube.com/watch?v=1Okmw8ggD1Q

Making Requests with Fetch API

To make a request with Fetch API, you can use the fetch() function, which returns a Promise:

fetch("<https://api.example.com/data>")
  .then((response) => {
    return response.json();
  })
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

In this example, we chain the then() method to handle the response and parse the JSON data. We also use the catch() method to handle network errors.

Making GET Requests

Making a GET request with Fetch API is quite simple:

fetch("<https://api.example.com/data>")
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.error("Error:", error));

This example is similar to the previous one, but with a more concise syntax.

Making POST Requests

To make a POST request with Fetch API, you need to pass an options object with the method and body properties:

const data = { key: "value" };

fetch("<https://api.example.com/data>", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify(data),
})
  .then((response) => {
    if (!response.ok) {
      throw new Error("HTTP error " + response.status);
    }
    console.log("Data created successfully");
  })
  .catch((error) => console.error("Error:", error));

In this example, we send a POST request to create new data on the server, similar to the XMLHttpRequest example.

Error Handling

With Fetch API, network errors are handled with the catch() method, while HTTP errors can be handled by checking the ok property of the response:

fetch("<https://api.example.com/data>")
  .then((response) => {
    if (!response.ok) {
      throw new Error("HTTP error " + response.status);
    }
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.error("Error:", error));

In this example, we handle both network and HTTP errors in a single catch() block.

Advanced Usage

Request and Response Objects

Fetch API provides Request and Response objects that allow you to configure request options and inspect response properties in a more granular way.

const request = new Request("<https://api.example.com/data>", {
  method: "POST",
  headers: new Headers({
    "Content-Type": "application/json",
  }),
  body: JSON.stringify(data),
});

fetch(request)
  .then((response) => {
    console.log("Status:", response.status);
    console.log("Headers:", response.headers);
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.error("Error:", error));

AbortController and Aborting Requests

To abort a Fetch API request, you can use the AbortController and AbortSignal:

const controller = new AbortController();
const signal = controller.signal;

fetch("<https://api.example.com/data>", { signal })
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => {
    if (error.name === "AbortError") {
      console.log("Request aborted");
    } else {
      console.error("Error:", error);
    }
  });

setTimeout(() => {
  controller.abort();
}, 5000);

In this example, we abort the request after 5 seconds.

Handling Timeouts

To implement a timeout for an XMLHttpRequest, you can use the timeout property:

xhr.timeout = 5000;
xhr.ontimeout = function () {
  console.error("Request timed out");
};

To implement a timeout for Fetch API, you can use Promise.race():

const timeoutPromise = new Promise((_, reject) =>
  setTimeout(() => reject(new Error("Request timed out")), 5000)
);

Promise.race([fetch("<https://api.example.com/data>"), timeoutPromise])
  .then((response)
=> response.json())
.then((data) => console.log(data))
.catch((error) => console.error("Error:", error));

In this example, we use Promise.race() to race the fetch request against a timeout promise. The first promise to settle (either resolve or reject) will win the race, resulting in either the fetched data or a timeout error.

Working with FormData

FormData is a convenient way to send form data as key-value pairs in an HTTP request. It can be used with both XMLHttpRequest and Fetch API.

Here's an example using XMLHttpRequest:


```javascript
const form = document.getElementById("myForm");
const formData = new FormData(form);

const xhr = new XMLHttpRequest();
xhr.open("POST", "<https://api.example.com/data>", true);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 201) {
    console.log("Data submitted successfully");
  }
};
xhr.send(formData);

And here's an example using Fetch API:

const form = document.getElementById("myForm");
const formData = new FormData(form);

fetch("<https://api.example.com/data>", {
  method: "POST",
  body: formData,
})
  .then((response) => {
    if (!response.ok) {
      throw new Error("HTTP error " + response.status);
    }
    console.log("Data submitted successfully");
  })
  .catch((error) => console.error("Error:", error));

In both examples, we create a FormData object from an HTML form and send it as the request body.

Best Practices

Using Asynchronous Code

Fetch API already uses Promises for handling asynchronous code, which makes it easier to write and maintain. You can further improve the readability of your code by using async/await:

async function fetchData() {
  try {
    const response = await fetch("<https://api.example.com/data>");
    if (!response.ok) {
      throw new Error("HTTP error " + response.status);
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error:", error);
  }
}

fetchData();

In this example, we use an async function and the await keyword to handle the fetch request and response.

Progress Indication

A good practice is to display a loading spinner or some other indication of progress during HTTP requests. This provides a better user experience and helps users understand that the application is fetching or updating data.

You can use CSS and JavaScript to show and hide a loading spinner based on the state of your XMLHttpRequest or Fetch API request.

CORS and Cross-Origin Requests

CORS (Cross-Origin Resource Sharing) is a security feature implemented by browsers that restricts web pages from making requests to a different domain than the one that served the web page. When making cross-origin requests with XMLHttpRequest or Fetch API, you might encounter CORS errors.

To fix these errors, you need to configure CORS headers on the server. For example, you can set the Access-Control-Allow-Origin header to allow specific origins or use a wildcard (*) to allow any origin.

Handling Browser Support

While Fetch API is widely supported in modern browsers, older browsers like Internet Explorer do not support it. To ensure your code works in all browsers, you can use feature detection and polyfills.

For example, you can use this code snippet to check if the browser supports Fetch API:

if (window.fetch) {
  // Use Fetch API
} else {
  // Use XMLHttpRequest or load a polyfill
}

You can also use a polyfill like fetch or unfetch to add Fetch API support to older browsers.

Conclusion

Implementing XMLHttpRequest and Fetch API is essential for making HTTP requests in web applications. This guide covered the basics of making requests, handling responses, and error handling with both APIs, along with advanced usage and best practices. Understanding both APIs and choosing the right one for your use case can help you build more robust and efficient web applications.

Frequently Asked Questions

1. When should I use XMLHttpRequest vs Fetch API?

Use XMLHttpRequest if you need to support older browsers like Internet Explorer or if you require features not available in Fetch API, such as progress events. Use Fetch API for modern browsers, as it provides a more powerful and flexible way of making HTTP requests with Promises and cleaner syntax.

2. How can I handle timeouts with XMLHttpRequest and Fetch API?

For XMLHttpRequest, use the timeout property and the ontimeout event listener. For Fetch API, use Promise.race() to race the fetch request against a timeout promise.

3. How do I send JSON data in a POST request?

For both XMLHttpRequest and Fetch API, set the "Content-Type" request header to "application/json" and send the JSON data as a string in the request body using JSON.stringify().

4. How can I handle CORS errors?

CORS errors can be resolved by configuring CORS headers on the server, such as setting the Access-Control-Allow-Origin header to allow specific origins or using a wildcard (*) to allow any origin.

5. What are some alternatives to XMLHttpRequest and Fetch API?

Some popular alternatives to XMLHttpRequest and Fetch API include libraries like Axios and jQuery.ajax(). These libraries provide additional features and a more convenient API for making HTTP requests in JavaScript.

Sign up for the Artisan Beta

Help us reimagine WordPress.

Whether you’re a smaller site seeking to optimize performance, or mid-market/enterprise buildinging out a secure WordPress architecture – we’ve got you covered. 

We care about the protection of your data. Read our Privacy Policy.