AJAX and JSON together form the backbone of modern web communication. This chapter dives deep into making HTTP requests from the browser, handling responses, and working with the JSON data format.
XMLHttpRequest is the original browser API for making HTTP requests. It is event-driven and supports both synchronous and asynchronous modes.
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1');
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
console.log('Title:', data.title);
} else {
console.error('Request failed:', xhr.status);
}
};
xhr.onerror = () => console.error('Network error');
xhr.send();
XMLHttpRequest does not support promises natively, making it verbose. It also ignores the Content-Type when using overrideMimeType(). Modern code should prefer the Fetch API.
The Fetch API provides a cleaner, promise-based interface for making HTTP requests. It is built into all modern browsers.
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(posts => console.log('Posts:', posts))
.catch(err => console.error('Fetch error:', err));
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'New Post',
body: 'This is the content.',
userId: 1
})
})
.then(res => res.json())
.then(data => console.log('Created:', data))
.catch(err => console.error(err));
The Response object provides several methods to read the body in different formats:
fetch('/data')
.then(res => {
console.log('Status:', res.status);
console.log('Headers:', res.headers.get('Content-Type'));
return res.text(); // or .json(), .blob(), .formData()
})
.then(data => console.log(data));
Network failures and non-2xx status codes must be handled explicitly.
async function fetchData(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`Status ${res.status}`);
return await res.json();
} catch (err) {
console.error('Failed to fetch:', err.message);
return null;
}
}
JSON supports these data types: string, number, boolean, null, object, and array. All keys and string values must be enclosed in double quotes.
{
"string": "Hello",
"number": 42,
"boolean": true,
"nullValue": null,
"array": [1, 2, 3],
"object": { "nested": "value" }
}
JSON is more concise, easier to parse, and maps directly to JavaScript objects. XML is more verbose but supports attributes, namespaces, and schema validation.
<!-- XML version of the same data -->
<person>
<name>Alice</name>
<age>30</age>
</person>
// JSON equivalent
{"name": "Alice", "age": 30}
JSON.stringify(value, replacer, space) to pretty-print JSON with indentation. The space parameter controls the indentation width.
REST APIs expose resources via standard HTTP methods. A typical workflow involves fetching a list, creating a new resource, updating one, and deleting it.
const BASE = 'https://jsonplaceholder.typicode.com';
async function apiDemo() {
// GET all
const todos = await fetch(`${BASE}/todos?_limit=3`).then(r => r.json());
console.log('Todos:', todos);
// POST create
const created = await fetch(`${BASE}/todos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Learn Fetch', completed: false })
}).then(r => r.json());
console.log('Created:', created);
}
Async/await makes asynchronous fetch code read like synchronous code, improving readability.
async function getUser(id) {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!res.ok) throw new Error('User not found');
return await res.json();
}
(async () => {
try {
const user = await getUser(1);
console.log(user.name);
} catch (err) {
console.error(err);
}
})();
Create a page with an input field and a button. When the user types a GitHub username and clicks the button, use the Fetch API to call https://api.github.com/users/{username}. Display the user’s avatar, name, bio, and public repository count. Handle errors gracefully if the user is not found.