Skip to content

AJAX and Fetch - Interacting with Web Servers

Modern web applications heavily rely on network communication to fetch or send data to servers dynamically. This is where AJAX (Asynchronous JavaScript and XML) and its evolution with the modern fetch API come into play.

AJAX, short for Asynchronous JavaScript and XML, is a technique that allows web pages to communicate with a server without reloading the page. It enables the creation of dynamic, responsive web experiences by handling asynchronous requests to fetch or send data.

You should use AJAX (and similar modern techniques like fetch) to retrieve or send data to a server dynamically (e.g., for live updates or form submissions).

The XMLHttpRequest (XHR) object was the original way to implement AJAX. Although outdated today, it’s still useful to understand its basics for legacy code or compatibility purposes.

const xhr = new XMLHttpRequest();
// Open a new GET request
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts");
// Set up a callback for when the response is ready
xhr.onload = () => {
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText); // Parse JSON response
console.log(data);
} else {
console.error("Request failed:", xhr.statusText);
}
};
// Send the request
xhr.send();

However, XMLHttpRequest comes with some limitations, such as a verbose syntax and lack of support for promises.

Modern browser support fetch() API to overcome the above mentioned limitations.

The fetch() API is the modern, promise-based way to make HTTP requests. It offers a simpler, more flexible syntax compared to XMLHttpRequest and is well-suited for modern JavaScript workflows.

Since it’s promise-based, we can have chaining and error handling cleaner and more intuitive way. Supports async/await for a synchronous-like code flow.

fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok!");
}
return response.json(); // Parse JSON response
})
.then((data) => {
console.log(data); // Handle the fetched data
})
.catch((error) => {
console.error("Fetch error:", error);
});

Parsing JSON, Error Handling, and Chaining Requests

Section titled “Parsing JSON, Error Handling, and Chaining Requests”

Most web APIs respond with data in JSON format. Both fetch and XMLHttpRequest require you to explicitly parse the JSON.

fetch example

fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => response.json()) // Parse JSON directly
.then((data) => console.log(data));

Error handling in fetch() is done using .catch() for network errors. However, since fetch() resolves on any response (even 404s), you should also check the response status.

Example:

fetch("https://jsonplaceholder.typicode.com/invalid-endpoint")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then((data) => console.log(data))
.catch((error) => console.error("Fetch error:", error));

Sometimes you may need to make multiple requests in sequence. This is easy to do with promise chaining or async/await.

Example:

fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => response.json())
.then((post) => {
console.log(`Post title: ${post.title}`); // Fetch comments for the post
return fetch(`https://jsonplaceholder.typicode.com/posts/1/comments`);
})
.then((response) => response.json())
.then((comments) => console.log("Comments:", comments))
.catch((error) => console.error("Error:", error));

Or using async/await:

async function fetchPostAndComments() {
try {
const postResponse = await fetch(
"https://jsonplaceholder.typicode.com/posts/1",
);
if (!postResponse.ok) throw new Error("Failed to fetch post!");
const post = await postResponse.json();
console.log(`Post title: ${post.title}`);
const commentsResponse = await fetch(
"https://jsonplaceholder.typicode.com/posts/1/comments",
);
if (!commentsResponse.ok) throw new Error("Failed to fetch comments!");
const comments = await commentsResponse.json();
console.log("Comments:", comments);
} catch (error) {
console.error("Error:", error);
}
}
fetchPostAndComments();