Making HTTP Requests in JavaScript
intermediate
This guide covers both the modern fetch
API and the legacy XMLHttpRequest
object for making HTTP requests in JavaScript. We’ll explore how to use both approaches and their various features.
The Fetch API
Basic Usage
// Simple GET request
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Using async/await
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
catch (error) {
} console.error('Error:', error);
} }
Request Methods and Options
// POST request with JSON data
async function postData(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here'
,
}body: JSON.stringify(data)
;
})return response.json();
}
// PUT request
async function updateData(url, data) {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
,
}body: JSON.stringify(data)
;
})return response.json();
}
// DELETE request
async function deleteData(url) {
const response = await fetch(url, {
method: 'DELETE'
;
})return response.status === 204;
}
Handling Different Response Types
// JSON response
async function fetchJSON() {
const response = await fetch('/api/data');
return response.json();
}
// Text response
async function fetchText() {
const response = await fetch('/api/text');
return response.text();
}
// Binary data (Blob)
async function fetchImage() {
const response = await fetch('/api/image');
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
return imageUrl;
}
// Form data
async function fetchFormData() {
const response = await fetch('/api/form');
return response.formData();
}
Error Handling and Response Checking
async function fetchWithErrorHandling(url) {
try {
const response = await fetch(url);
// Check if response is ok (status in 200-299 range)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Check content type
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new TypeError("Expected JSON response");
}
return await response.json();
catch (error) {
} console.error('Error:', error);
throw error;
} }
Sending Form Data
// Sending form data
async function submitForm(formData) {
const response = await fetch('/api/submit', {
method: 'POST',
body: formData // FormData object
;
})return response.json();
}
// Sending file
async function uploadFile(file) {
const formData = new FormData();
.append('file', file);
formData
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
;
})return response.json();
}
XMLHttpRequest Object
Basic Usage
function makeRequest(method, url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
.open(method, url);
xhr
.onload = function() {
xhrif (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
else {
} reject({
status: xhr.status,
statusText: xhr.statusText
;
})
};
}
.onerror = function() {
xhrreject({
status: xhr.status,
statusText: xhr.statusText
;
});
}
.send();
xhr;
}) }
POST Request with XMLHttpRequest
function postData(url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr
.onload = function() {
xhrif (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response));
else {
} reject({
status: xhr.status,
statusText: xhr.statusText
;
})
};
}
.onerror = function() {
xhrreject({
status: xhr.status,
statusText: xhr.statusText
;
});
}
.send(JSON.stringify(data));
xhr;
}) }
Progress Monitoring with XMLHttpRequest
function uploadWithProgress(file, progressCallback) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const formData = new FormData();
.append('file', file);
formData
.open('POST', '/api/upload');
xhr
.upload.onprogress = function(event) {
xhrif (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
progressCallback(percentComplete);
};
}
.onload = function() {
xhrif (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response));
else {
} reject({
status: xhr.status,
statusText: xhr.statusText
;
})
};
}
.onerror = function() {
xhrreject({
status: xhr.status,
statusText: xhr.statusText
;
});
}
.send(formData);
xhr;
})
}
// Usage
uploadWithProgress(file, (progress) => {
console.log(`Upload progress: ${progress}%`);
.then(response => {
})console.log('Upload complete:', response);
.catch(error => {
})console.error('Upload failed:', error);
; })
Practical Examples
1. Retry Mechanism
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}return await response.json();
catch (error) {
} if (attempt === maxRetries) throw error;
// Wait before retrying (exponential backoff)
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
}
} }
2. Request Queue
class RequestQueue {
constructor(concurrency = 3) {
this.concurrency = concurrency;
this.queue = [];
this.running = 0;
}
async enqueue(request) {
return new Promise((resolve, reject) => {
this.queue.push({
,
request,
resolve
reject;
})this.processQueue();
;
})
}
async processQueue() {
if (this.running >= this.concurrency || this.queue.length === 0) {
return;
}
this.running++;
const { request, resolve, reject } = this.queue.shift();
try {
const response = await fetch(request);
const data = await response.json();
resolve(data);
catch (error) {
} reject(error);
finally {
} this.running--;
this.processQueue();
}
}
}
// Usage
const queue = new RequestQueue(2);
const requests = [
new Request('https://api.example.com/1'),
new Request('https://api.example.com/2'),
new Request('https://api.example.com/3')
;
]
.forEach(request => {
requests.enqueue(request)
queue.then(data => console.log('Request complete:', data))
.catch(error => console.error('Request failed:', error));
; })
Best Practices
- Error Handling
- Always include error handling
- Check response.ok with fetch
- Validate response content type
- Request Timeouts
- Implement timeout mechanisms
- Use AbortController for fetch
- Set xhr.timeout for XMLHttpRequest
- Security
- Always validate and sanitize data
- Use HTTPS
- Implement proper CORS headers
- Performance
- Use request queuing for multiple requests
- Implement retry mechanisms
- Cache responses when appropriate