Skip to main content

AJAX

AJAX (Asynchronous JavaScript and XML) is a method of receiving or sending data, and then updating the interface according to this data, without having to reload the page. It reduces response times and makes the web page more interactive. This process can be explained on the example of loading data.

  1. An event occurs on the web page (page loading, clicking the Learn More button, form submission, etc).
  2. In response to this event, the client, using JavaScript, executes a function to work with the server, where the HTTP request is created and sent.
  3. The server receives and processes the HTTP request by sending back JSON data in its response.
  4. The client, using JavaScript, processes the server response, reads the data and updates the interface.
Note

Despite that the technology name features XML, it was replaced by JSON, and the name was left as a tribute. AJAX means any communication with the server without reloading the page.

Fetch API

A browser-built interface available on the window object that contains a set of properties and methods for sending, receiving and processing resources from the server. The fetch() method provides a modern interface for making requests to the server, and it is based on promises.

fetch(url, options)
  • url is the path to the back-end data that needs to be retrieved, created or modified. Mandatory argument.
  • options contains optional parameters of requests – methods (GET by default), headings, body, etc. Optional argument.

Let’s make requests to JSONPlaceholder API, a public REST API for quick prototyping that provides a collection of fake users in the /users resource.

fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
// Response handling
})
.then(data => {
// Data handling
})
.catch(error => {
// Error handling
});

Response check

The promise value returned by the fetch() method is an object with additional information about the server response status. It is an instance of the Response class provided with various methods and properties. Depending on the type of received content, a different method is used to transform the response body into data.

  • json() parses data in JSON format.
  • text() parses data in plain text format, for example .csv (tabular data).
  • blob() parses data describing the file, e.g. image, audio or video.

The first then() method checks the response status and converts the data to the correct format, or explicitly throws an error to handle the failed HTTP request in the catch() block.

fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
if (!response.ok) {
throw new Error(response.status);
}
return response.json();
})
.then(data => {
// Data handling
})
.catch(error => {
// Error handling
});
Note

This is necessary in order for fetch() to respond correctly to the 404 status code, which is technically not an error, but neither is a successful result for the client.

Network tab

In the developer tools, the Network tab displays all HTTP requests made on the page. With the XHR filter, you will leave only requests to the back-end. When clicking the button in the example, after a while, the request will be displayed in the list. By selecting it, you can view additional information and the response body in the Headers, Preview and Response sub-tabs.

Network tab in Chrome developer tools

Work with public REST API

Each back-end is unique, there are thousands of them. On the other hand, REST APIs have a standard architecture. This means that you should first understand how they work, after which all you need to do is to study the documentation of the back-end that you need to use.

Let's continue work with JSONPlaceholder API. From the documentation, take the URL of the resource to request information about the collection of fake users.

https://jsonplaceholder.typicode.com/users

This path consists of the following parts:

  • https://jsonplaceholder.typicode.com is the endpoint, base URL, API entry point.
  • /users is the resource you are accessing.

By clicking the "Fetch users" button, make a GET request and compile a list of users based on the received data.

Note

Note that the fetchUsers() function returns a promise, so add a chain of then() and catch() methods to the result of calling it.

Rookie mistake

Let's look at a common mistake when working with asynchronous code – trying to use the data of an HTTP request outside of the callback of the then() method. Rookies try to write the "fetch result" to an external variable and use it further down the code immediately after calling the fetch() method.

let globalVariable; // undefined

// Initializing data fetching
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(users => {
console.log("users inside then callback: ", users);

// Writing the result to a global variable
globalVariable = users;

// Everything is ok here, the data is in the variable
console.log("globalVariable inside fetch callback: ", globalVariable);
});

// No async data here
console.log("globalVariable outside fetch: ", globalVariable); // undefined

Outside the callback of the then() method, there is no data, since the last console.log() will have been executed by the time the response from the server arrives. At this time, the variable globalVariable still contains undefined. The value of the fulfilled promise is available only in the then() method’s callback.

Query string parameters

Query string parameters are used to specify additional criteria for the back-end. For example, how many collection items you want to receive in your request; perhaps you need to add sorting by some object property, restrict selection, etc. The list of query string parameters, their names and possible values depend on the back-end and are described in the documentation.

The ? character marks the start of parameters. Each parameter is a name=value pair. The & character is used to indicate "AND", separating parameters in the query string.

const url = "https://jsonplaceholder.typicode.com/users?_limit=7&_sort=name";

Such GET request will return an array of seven users (10 in total) sorted alphabetically by name (name field). The underscores in parameter names are specific to this back-end, this is not some kind of standard.

URLSearchParams class

There can be many parameters, and it is not convenient to make one long line from them, both in terms of readability and subsequent editing. When making a parameter string, an instance of the URLSearchParams class is created and initialized as an object. The result will be a special object (iterator) with methods; in string conversion, it returns the result of calling the toString() method – its own string representation.

const searchParams = new URLSearchParams({
_limit: 5,
_sort: "name",
});

console.log(searchParams.toString()); // "_limit=5&_sort=name"

const url = `https://jsonplaceholder.typicode.com/users?${searchParams}`;
console.log(url); // "https://jsonplaceholder.typicode.com/users?_limit=5&_sort=name"

In string form, object properties will become parameters and their values. Parameters will be separated by &. When interpolating a value in template strings, it is implicitly converted to a string, so you do not need to call the toString() method when creating a URL. Do not forget to mark the beginning of the query string with ?.

HTTP headers

The Headers class enables you to perform various actions in the HTTP request and response headers. These actions include retrieving, modifying, adding and removing headers.

const headers = new Headers({
"Content-Type": "application/json",
"X-Custom-Header": "custom value",
});

headers.append("Content-Type", "text/bash");
headers.append("X-Custom-Header", "custom value");
headers.has("Content-Type"); // true
headers.get("Content-Type"); // "text/bash"
headers.set("Content-Type", "application/json");
headers.delete("X-Custom-Header");

In practice, a simple object literal with properties is commonly used to make up request headers. In this case, there will be no methods, as often they are not required.

const headers = {
"Content-Type": "application/json",
"X-Custom-Header": "custom value",
};

A request using headers will look like this.

fetch("https://jsonplaceholder.typicode.com/users", {
headers: {
Accept: "application/json",
},
}).then(response => {
// ...
});
Note

Modern browsers add many headers by default, depending on the operation and the request body, so there is no need to explicitly specify standard headers.