Learn how to use the Fetch API to make GET, POST, PUT, and DELETE requests. Includes CSRF handling for Django backends.
The Fetch API is the modern way to make HTTP requests in JavaScript. It replaced XMLHttpRequest (XHR) and provides a cleaner, Promise-based interface. This tutorial covers everything from basic GET requests to handling CSRF tokens in Django.
// Simple GET
const response = await fetch('/api/products/');
const data = await response.json();
console.log(data);
// With error handling
async function fetchProducts() {
try {
const response = await fetch('/api/products/');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch failed:', error);
}
}
fetch() only rejects on network errors, NOT on HTTP error status codes (404, 500, etc.). Always check response.ok or response.status.
async function createProduct(productData) {
const response = await fetch('/api/products/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(productData),
});
if (!response.ok) {
const errors = await response.json();
console.error('Validation errors:', errors);
return;
}
const newProduct = await response.json();
console.log('Created:', newProduct);
}
// Usage
createProduct({
title: 'New Widget',
price: '29.99',
category: 1
});
// PUT - full update
await fetch('/api/products/1/', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Updated', price: '39.99', category: 1 }),
});
// PATCH - partial update
await fetch('/api/products/1/', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ price: '19.99' }),
});
// DELETE
await fetch('/api/products/1/', {
method: 'DELETE',
});
When making POST/PUT/DELETE requests to Django views (not DRF), you need to include the CSRF token:
// Get CSRF token from cookie
function getCookie(name) {
let cookieValue = null;
if (document.cookie) {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.startsWith(name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// Use in fetch
await fetch('/contact/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken'),
},
body: JSON.stringify({ message: 'Hello!' }),
});
const csrfToken = '{{ csrf_token }}';
// FormData - for file uploads and multipart forms
const form = document.querySelector('#myForm');
const formData = new FormData(form);
await fetch('/upload/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
},
body: formData, // Don't set Content-Type - browser sets it with boundary
});
const button = document.querySelector('#submitBtn');
const spinner = document.querySelector('#spinner');
const message = document.querySelector('#message');
button.addEventListener('click', async () => {
// Show loading
button.disabled = true;
spinner.style.display = 'inline-block';
message.textContent = '';
try {
const response = await fetch('/api/action/', { method: 'POST' });
const data = await response.json();
message.textContent = 'Success!';
message.className = 'text-success';
} catch (error) {
message.textContent = 'Something went wrong.';
message.className = 'text-danger';
} finally {
button.disabled = false;
spinner.style.display = 'none';
}
});
const api = {
async request(url, options = {}) {
const defaults = {
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken'),
},
};
const response = await fetch(url, { ...defaults, ...options });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
},
get(url) { return this.request(url); },
post(url, data) { return this.request(url, { method: 'POST', body: JSON.stringify(data) }); },
patch(url, data) { return this.request(url, { method: 'PATCH', body: JSON.stringify(data) }); },
delete(url) { return this.request(url, { method: 'DELETE' }); },
};
// Usage
const books = await api.get('/api/books/');
await api.post('/api/books/', { title: 'New Book', price: '19.99' });
await api.delete('/api/books/5/');
fetch() for all HTTP requests in modern JavaScriptresponse.ok — fetch doesn't throw on HTTP errorsasync/await for cleaner code instead of .then() chains