π Modern Ajax Made Simple
XMLHttpRequest? 10 lines for simple GET? Callback hell? Fetch API is promise-based, clean syntax, built-in JSON parsing. Modern Ajax the right way.
The Old Way (XMLHttpRequest)
// β XMLHttpRequest - Verbose and ugly
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/users');
xhr.onload = function() {
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log(data);
} else {
console.error('Request failed');
}
};
xhr.onerror = function() {
console.error('Network error');
};
xhr.send();
// 10+ lines for a simple GET request!
The Modern Way (Fetch API)
// β
Fetch API - Clean and promise-based
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Or with async/await (even cleaner!)
async function getUsers() {
try {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
π― Common Fetch Patterns
GET Request
// Simple GET
const response = await fetch('https://api.example.com/users');
const users = await response.json();
// GET with query parameters
const params = new URLSearchParams({ page: 1, limit: 10 });
const response = await fetch(`https://api.example.com/users?${params}`);
POST Request
const newUser = {
name: 'Alice',
email: 'alice@example.com'
};
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newUser)
});
const data = await response.json();
console.log('Created user:', data);
PUT/PATCH Request
// Update user
const response = await fetch('https://api.example.com/users/123', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Updated Name' })
});
DELETE Request
const response = await fetch('https://api.example.com/users/123', {
method: 'DELETE'
});
if (response.ok) {
console.log('User deleted');
}
Authentication & Headers
// Bearer token authentication
const response = await fetch('https://api.example.com/protected', {
headers: {
'Authorization': 'Bearer your-token-here',
'Content-Type': 'application/json'
}
});
// API key
const response = await fetch('https://api.example.com/data', {
headers: {
'X-API-Key': 'your-api-key'
}
});
// Custom headers
const response = await fetch('https://api.example.com/data', {
headers: {
'X-Custom-Header': 'value',
'Accept-Language': 'en-US'
}
});
β οΈ Error Handling
// IMPORTANT: Fetch only rejects on network error, not HTTP errors!
async function fetchWithErrorHandling() {
try {
const response = await fetch('https://api.example.com/users');
// Check if response is OK (status 200-299)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}
// Handle different status codes
const response = await fetch('https://api.example.com/users');
switch (response.status) {
case 200:
const data = await response.json();
console.log('Success:', data);
break;
case 404:
console.error('Not found');
break;
case 401:
console.error('Unauthorized');
break;
case 500:
console.error('Server error');
break;
default:
console.error('Unexpected status:', response.status);
}
Advanced Features
// Timeout (using AbortController)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://api.example.com/slow', {
signal: controller.signal
});
clearTimeout(timeoutId);
const data = await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.error('Request timed out');
}
}
// Upload file
const formData = new FormData();
formData.append('file', fileInput.files[0]);
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData // No Content-Type header needed!
});
// Download blob (image, PDF, etc.)
const response = await fetch('https://api.example.com/image.jpg');
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
document.getElementById('img').src = imageUrl;
// Stream large response
const response = await fetch('https://api.example.com/large-data');
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Received chunk:', value);
}
β Benefits Over XMLHttpRequest
- Promise-based: Works with async/await
- Cleaner syntax: Less boilerplate code
- Better error handling: Try/catch works naturally
- Built-in features: JSON parsing, blob handling
- Streaming: Can process large responses in chunks
- Modern standard: Supported in all browsers
π‘ Pro Tips
- Always check response.ok: Fetch doesn’t reject on 404, 500
- Use AbortController: For timeouts and cancellation
- Cache responses: Use cache: ‘force-cache’ option
- CORS: Use mode: ‘cors’ for cross-origin requests
- Credentials: credentials: ‘include’ to send cookies
π― Complete Example
class ApiClient {
constructor(baseURL, token) {
this.baseURL = baseURL;
this.token = token;
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`,
...options.headers,
},
};
try {
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
get(endpoint) {
return this.request(endpoint);
}
post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data),
});
}
put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data),
});
}
delete(endpoint) {
return this.request(endpoint, {
method: 'DELETE',
});
}
}
// Usage
const api = new ApiClient('https://api.example.com', 'your-token');
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'Alice' });
“Refactored entire codebase from XMLHttpRequest to Fetch. Code reduced by 40%. No more callback pyramids. Async/await made everything readable. Best decision for project health.”
