How to Make AJAX Calls in JavaScript How to Make AJAX Calls in JavaScript

Page content

In this tutorial, we’ll learn how to make AJAX calls in JavaScript.

AJAX

The term AJAX stands for Asynchronous JavaScript And XML

The term AJAX is used in JavaScript for making asynchronous network request to fetch resources. Resources are not limited to XML, as the term suggest which is confusing. The term AJAX is also used to fetch resources as JSON, HTML, or Plain Text. You may have heard that term already.

There are multiple ways to make network request and fetch data from the server. We’ll look at them one by one.

XMLHttpRequest

The XMLHttpRequest object (also known as XHR in short) is used in older days to retrieve data from the server asynchronously.

The XML comes in the name because it is first used to retrieve XML data. Now this can also be used to retrieve JSON, HTML or Plain text.

Example: GET

function success() {
    var data = JSON.parse(this.responseText);
    console.log(data);
}

function error(err) {
    console.log('Error Occurred :', err);
}

var xhr = new XMLHttpRequest();
xhr.onload = success;
xhr.onerror = error;
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1");
xhr.send();

We see that to make a simple GET request, we need two listeners to handle success and failure of the request. We also need to make calls to open() and send() methods. The response from the server is stored in the responseText variable, which is converted to JavaScript object using JSON.parse().

Example: POST

function success() {
    var data = JSON.parse(this.responseText);
    console.log(data);
}

function error(err) {
    console.log('Error Occurred :', err);
}

var xhr = new XMLHttpRequest();
xhr.onload = success;
xhr.onerror = error;
xhr.open("POST", "https://jsonplaceholder.typicode.com/posts");
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xhr.send(JSON.stringify({
    title: 'foo',
    body: 'bar',
    userId: 1
  })
);

We see that POST request is similar to GET request. We need to additionally set the request header “Content-Type” using setRequestHeader and send the JSON body as string using JSON.stringify in the send method.

XMLHttpRequest vs Fetch

We have used XMLHttpRequest for several years to request data. The modern fetch() API allows you to make network requests similar to XMLHttpRequest (XHR). The main difference is that the fetch() API uses Promises, which enables a simpler and cleaner API, avoiding callback hell and having to remember the complex API of XMLHttpRequest.

Fetch API

Fetch is a native JavaScript API to make AJAX calls which is supported by most of the browsers and widely used now a days.

API Usage

fetch(url, options)
    .then(response => {
        // handle response data
    })
    .catch(err => {
        // handle errors
    });

API Arguments

The fetch() API takes two arguments:

  1. url is a mandatory argument, which is a path to the resource you want to fetch.
  2. options is an optional argument. You don’t need to provide this argument to make simple GET request. You need to pass this argument to provide additional information about the request such as
    • method: GET | POST | PUT | DELETE | PATCH
    • headers: Request Headers Object for e.g. { “Content-type”: “application/json; charset=UTF-8” }
    • mode: cors | no-cors | same-origin | navigate
    • cache: default | reload | no-cache
    • body: Request body generally used in POST request

API returns Promise Object

The fetch() API returns a promise object.

  • The promise is rejected if there is a network error, this is handled in the .catch() block.
  • The promise is resolved if there is a response from the server with any status code such as 200, 404, 500. Response Object can be handled in the .then() block.

Error Handling

Please note that we expect status code as 200 (OK status) for successful response but fetch() API resolved the promise even if response is coming with error status code such as 404 (Resource Not found) and 500 (Internal Server Error). We need to handle those explicitly in .then() block.

We can see HTTP-status in response object:

  • status – HTTP status code, e.g. 200.
  • ok – boolean, true if the HTTP status code is 200-299.

Example: GET

const getTodoItem = fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json())
  .catch(err => console.error(err));

getTodoItem.then(response => console.log(response));
Response
➤ { userId: 1, id: 1, title: "delectus aut autem", completed: false }

Two things to note in above code:-

  1. fetch API returns a promise object which we can assign to a variable and execute later.
  2. You have to additionally call response.json() to convert response object to JSON

Error Handling

Let’s see what happens when HTTP GET request throw 500 error:-

fetch('http://httpstat.us/500') // this API throw 500 error
  .then(response => () => {
    console.log("Inside first then block");
    return response.json();
  })
  .then(json => console.log("Inside second then block", json))
  .catch(err => console.error("Inside catch block:", err));
Inside first then block
➤ ⓧ Inside catch block: SyntaxError: Unexpected token I in JSON at position 4

We see that even though API throw 500 error, It still goes inside first then() block where it’s not able to parse error JSON and throw error which is caught by catch() block.

That means we need to handle such errors explicitly like this if we are using fetch() API:-

fetch('http://httpstat.us/500')
  .then(handleErrors)
  .then(response => response.json())
  .then(response => console.log(response))
  .catch(err => console.error("Inside catch block:", err));

function handleErrors(response) {
  if (!response.ok) { // throw error based on custom conditions on response
      throw Error(response.statusText);
  }
  return response;
}
Inside catch block: Error: Internal Server Error at handleErrors (Script snippet %239:9)


Example: POST

fetch('https://jsonplaceholder.typicode.com/todos', {
    method: 'POST',
    body: JSON.stringify({
      completed: true,
      title: 'new todo item',
      userId: 1
    }),
    headers: {
      "Content-type": "application/json; charset=UTF-8"
    }
  })
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(err => console.log(err))
Response
➤ {completed: true, title: "new todo item", userId: 1, id: 201}

Two things to note in above code:-

  1. POST request is similar to GET request. We need to additionally send the method, body and headers properties in second argument of fetch() API.
  2. We have to explicitly JSON.stringify() the request body params

Axios API

Axios API is very similar to fetch API with few enhancements. I personally prefer to use Axios API instead of fetch() API due to following reasons:-

  1. Provides different methods for GET axios.get(), POST axios.post(), … which makes your code concise
  2. Consider 299++ response codes such as 404, 500 as errors which can be handled in catch() block so you do not need to handle those errors explicitly
  3. It provides backward compatibility with old browsers such as IE11
  4. It returns the response as JSON object so you do not need to do any parsing
  5. It takes the POST request body as JSON object so you do not need to do any stringify

Example: GET

// way to include script in chrome console
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://unpkg.com/axios/dist/axios.min.js';
document.head.appendChild(script);
axios.get('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => console.log(response.data))
  .catch(err => console.error(err));
Response
{ userId: 1, id: 1, title: "delectus aut autem", completed: false }

We see that we get the response data directly using response.data without any parsing object unlike fetch() API.

Error Handling
axios.get('http://httpstat.us/500')
  .then(response => console.log(response.data))
  .catch(err => console.error("Inside catch block:", err));
Inside catch block: Error: Network Error

We see that 500 error is also get caught by catch() block unlike fetch() API where we have to handle them explicitly.

Example: POST

axios.post('https://jsonplaceholder.typicode.com/todos', {
      completed: true,
      title: 'new todo item',
      userId: 1
  })
  .then(response => console.log(response.data))
  .catch(err => console.log(err))
➤ {completed: true, title: "new todo item", userId: 1, id: 201}

We see that POST method is very short and concise. You can directly pass the request body parameters unlike fetch() API where we stringify them.