Learn How to Make HTTP Requests With XMLHttpRequest, fetch() and async & await10 min read

Fetching and manipulating data through HTTP requests is an indispensable part of the Web-development. You might want to get the data through HTTP requests for a host of reasons, for example, get some data from the API, update or delete data via HTTP requests. When you click to a submit button, what happens under the hood, where does the information go, and how it is processed? The answer is all about the HTTP requests. There are many HTTP methods that we can use to work with data. However, in this article, we are going to learn the 2 most common HTTP requests which are GET and POST requests.

First, we need a basic understanding of how HTTP works over the internet, check out this article if you want. Since we mostly use the HTTP requests to manipulate the data from the API, then having a fundamental knowledge of REST principles is also great.

GET and POST Methods

GET – The GET method requests a representation of the specified resource. Requests using GET should only retrieve data and should have no other effect.

POST – The POST method is used to submit some data to a specific resource, often cause a change in state or side effect on the server-side. The submitted data usually lies in the body of the POST method.

REST principles

Before we jump into how to create some GET and POST requests in JavaScript, let’s briefly have a look at the REST principles. Representational State Transfer (REST) is a software architectural style that defines a set of constraints to be used for creating web services. The important fact about REST is it works over HTTP. Resources is the main concept of REST, and the resource here is defined by URLs. Each HTTP method defines a verb to work with this resource.

For example, Spotify API is based on a simple REST principle. https://api.spotify.com is a based URL that defines some resources such as releases, features, and music categories available on Spotify. An HTTP method like GET lets you get the data from this URL, such as a new release.

  • https://api.spotify.com/v1/browse/new-releases — with GET should return the new song and albums released on Spotify.
  • https://api.spotify.com/v1/browse/new-releases — with POST will create a new resource over https://api.spotify.com/v1/browse/new-releases.

Making HTTP requests in JavaScript is simple. There are a huge number of serviceable methods and modules readily in JavaScript which let us send or retrieve some data from a resource on the internet and put you at ease.

GET and POST Requests with AJAX

Asynchronous JavaScript and XML (AJAX) enables web applications to send and retrieve data from a server asynchronously. The XMLHttpRequest object is a key factor of AJAX, this object lets you send and retrieve XML data and other formats such as JSON from a server without refreshing the page.

Now let’s construct a typical GET request in JavaScript with XMLHttpRequest object:

// XMLHttpRequest GET

const xhr = new XMLHttpRequest();
const url = "http://example.com/endpoint";

xhr.responseType = 'json';
xhr.onreadystatechange = () => {
    if(xhr.readyState === XMLHttpRequest.DONE){
        // Code to execute with response
    }
}

xhr.open('GET', url);
xhr.send();

Let’s break down what’s happening here:

  • First, I create an XMLHttpRequest with a new keyword, you can name this object whatever you want, but it’s common practice to name variable as xhr.
  • On the next line, we store the url which will receive a GET request.
  • A XMLHttpRequest has a property named responseType which is how the response type is formatted. In the sixth line, the JSON format will be represented as a response type of our xhr object.
  • onreadystatechange is an event handler which is called when the ready state of the xhr object changed. 
  • Inside this event handler, the readyState property returns the state an XMLHttpRequest client is in, if it equals XMLHttpRequest.DONE which means the request has finished.
  • Right underneath this event handler, we call the open method on this object. This open method accepts two parameters, the request type, GET, and the specified URL to send the request to.
  • Finally, we send the request to the server with the send method. 

Let’s move on the POST request with an XMLHttpRequest object:

const xhr = new XMLHttpRequest();
const url = "http://example.com/endpoint"; 
const data = "Some data";

xhr.responseType = 'json';
xhr.onreadystatechange = () => {
    if(xhr.readyState === XMLHttpRequest.DONE){
        // Code to execute with response
    }
}
xhr.open('POST', url);
xhr.send(data);

As we can easily notice, the POST request looks pretty much the same as the GET request. There is a slight difference though, the GET request only lets us retrieve some data from a server and has no side-effect. The POST method will have some data come along when we send the request to the server.

On line third, we define a variable named data which stores some data. On line eleventh, we use the open method to initialize a new request, pass in to this method the ‘POST’ value as the first parameter and the URL. At the end of this code fragment, we send the data to the server.

Why have we passed an argument to xhr.send() with a POST request? This data we pass in will be posted to a resource and this resource will process the data and send the data back after the process. For example, when you enter an URL to a shorten URL website and click the “Submit” button, actually you’ve created a POST request. The data is sent to the server here is the URL you typed in and the server will process this POST request and give you back the shorten URL.

GET and POST Request with fetch()

An alternative way that you can use to make HTTP requests on the Web browser is the Fetch API. The Fetch API is a new and modern interface that allows you to make HTTP requests in the web browsers. The fetch() method from the Fetch API can perform all tasks of an XMLHttpRequest objects. By using fetch(), we can send an AJAX request or network request to a server and get the JSON response. Since the fetch() method returns a promise, your code can look more readable and callback hell is avoidable.

Basic Syntax

fetch(url,[options])

The fetch() function takes 2 parameters, the first one will be the url we want to send and retrieve data. The second one is optional, which can be a method, header, etc…

Let’s create a GET request with the fetch() function:

fetch("http://example.com/endpoint")
  .then(response => {
      if (response.ok) {
        return response.json();
      }
    }
  )
  .then(jsonResponse => {
    return jsonResponse;
  }).catch(networkError => {
      console.log(networkError.message);
  });

How the fetch function works:

  • First, we pass an URL which is the resource we want to work with as the first parameter of the fetch method.
  • After that, this method returns a promise, when the request is completed which means this resource is available, this promise is resolved with a Response object.
  • We pass this Response object as an argument of the then method as response.
  • This Response object has a number of handy properties, some of them are ok and .json(), this response.ok will return true if the response returned successfully (the status code from 200-299).
  • If it is true, we use the .json() method to convert the response data to the JSON format and return this format.
  • This response.json() method also returns a promise, that why we can chain another then method once again, inside the second then, we just simply return the data in JSON format jsonResponse.
  • If there is any error occurs in the first promise or the second one, then we can use the catch method to handle the reject state of the promise which causes an error.

As we can observe, with the fetch() method, our code now looks more concise and readable compared to thenXMLHttpRequest version. There is no open() or send() methods, just simple and versatile promises.

Now let’s learn how to create a POST request with fetch() method:

fetch("http://example.com/endpoint", {
    method: 'POST',
    body: 'Some cool data'
}).then(response => {
    if(response.ok){
        return response.json();
    }
}).then(jsonResponse => jsonResponse)
.catch(error => console.log(error));

To make a POST request to the server, we pass the second argument to the fetch method, this object we passed in determine the information we want to send the the server. This object has 2 properties, method defines the type of HTTP request and body is the data we want to send. Henceforth, every thing is identical to the GET version we have done. The body of each method can contain arbitrary code, here I just simple return an objects in each method for simplicity.

The fetch() method and async & await

Because the fetch() method returns a promise, and this promise has to be resolved before we want to do any further actions, in harmony with this rhythm, the concept of async and await function comes in handy.

The async function returns a promise, and the await keyword only exists inside the async function. Basically, the await keyword is placed right before a promise. It will halt the execution of the program until the promise is resolved! There would be no point of we haven’t obtained a resolved promise and doing some actions at the same time. With async and await keyword, callback hell is totally avoidable.

Let’s construct a sample GET request with fetch(), async and await:

const getData = async () => {
    try{
        const response = await fetch("http://example.com/endpoint");
        if(response.ok){
            const jsonResponse = await response.json();
            return jsonResponse;
        }
    }catch(error){
        console.log(error);
    }
}

How it works:

  • We create a function getData(), this will be called when we want to get some data from http://example.com/endpoint.
  • The code in the try block will send a request and handle the response. The catch statement will then take care of an error if it is thrown.
  • Inside the try block, the fetch() method returns a promise, the await keyword placed right before to beckon that this promise need to be resolved before move on the next lines and we save this promise to response.
  • If the request successfully finished, inside the if statement, we have another variable named jsonResponse, response.json() also returns a promise, so again we use the await keyword to make sure this promise is resolved before moving on.
  • Then at the end of the if statement, we return the formatted JSON response.
  • If there is any error occurs in the try block, the catch block will log the errors.

What about the POST request with fetch(), async and await:

const postData = async () => {
    try{
        const response = await fetch('http://example.com/endpoint', {
            method: 'POST',
            body: 'some cool data'
        });
        if(response.ok){
            const jsonResponse = await response.json();
            return jsonResponse;
        }
    }catch(error){
        console.log(error);
    }
}

To make a POST request, we use the second argument to send the data to the server, then we can do exactly the same as we did with the GET request.

Summary

In this article, we have learned some of the most common ways to make HTTP requests in JavaScript. The XMLHttpRequest is the old and traditional way to make HTTP requests, fetch() is a modern one which returns a promise. A GET method will retrieve the data and have no other effects, the POST method usually contains some data in the body to send to the server.

Previous Article
Next Article
Every support is much appreciated ❤️

Buy Me a Coffee