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. In this blog, we’ll cover the following:
- What AJAX is and when to use it.
- Basics of
XMLHttpRequest
(the legacy method). - Using the modern
fetch()
API. - How to handle JSON, errors, and chaining requests.
1. What is AJAX and When to Use It?
Section titled “1. What is AJAX and When to Use It?”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
) when:
- You need to retrieve or send data to a server dynamically (e.g., for live updates or form submissions).
- You want to interact with APIs (Application Programming Interfaces).
- You want to enhance user experience by avoiding full-page reloads.
Example use cases:
- Loading additional content when users scroll.
- Submitting a form and displaying results without leaving the page.
- Fetching live data, like weather or stock prices.
2. XMLHttpRequest Basics (Legacy Method)
Section titled “2. XMLHttpRequest Basics (Legacy Method)”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.
Here’s an example of how to use XMLHttpRequest
to fetch data:
const xhr = new XMLHttpRequest();// Open a new GET requestxhr.open( "GET", "[https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts)",);// Set up a callback for when the response is readyxhr.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 requestxhr.send();
However, XMLHttpRequest
comes with some limitations, such as a verbose syntax and lack of support for promises. This is where the modern fetch()
API shines.
3. Using the Modern fetch()
API
Section titled “3. Using the Modern fetch() API”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.
Basic Example of fetch()
:
Section titled “Basic Example of fetch():”fetch( "[https://jsonplaceholder.typicode.com/posts](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); });
Key Features of fetch()
:
Section titled “Key Features of fetch():”- Promise-based: Makes chaining and error handling cleaner and more intuitive.
- Supports modern async/await: Allows for a synchronous-like code flow.
4. Parsing JSON, Error Handling, and Chaining Requests
Section titled “4. Parsing JSON, Error Handling, and Chaining Requests”Parsing JSON:
Section titled “Parsing JSON:”Most web APIs respond with data in JSON format. Both fetch
and XMLHttpRequest
require you to explicitly parse the JSON.
Example:
fetch( "[https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts)",) .then((response) => response.json()) // Parse JSON directly .then((data) => console.log(data));
Handling Errors:
Section titled “Handling Errors:”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](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));
Chaining Requests:
Section titled “Chaining Requests:”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](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](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();