JavaScript Beginner

Fetch API: Making HTTP Requests in JavaScript

Learn how to use the Fetch API to make GET, POST, PUT, and DELETE requests. Includes CSRF handling for Django backends.

DjangoZen Team Mar 29, 2026 4 min read 25 views

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.

Basic GET Request

// 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);
    }
}
Important: fetch() only rejects on network errors, NOT on HTTP error status codes (404, 500, etc.). Always check response.ok or response.status.

POST Request

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, PATCH, DELETE

// 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',
});

Django CSRF Token with Fetch

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!' }),
});
Django Tip: If you're using Django templates, you can also get the token from the template tag: const csrfToken = '{{ csrf_token }}';

Sending Form Data

// 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
});

Loading States & UI Pattern

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';
    }
});

Reusable API Client

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/');

Summary

  • Use fetch() for all HTTP requests in modern JavaScript
  • Always check response.ok — fetch doesn't throw on HTTP errors
  • Use async/await for cleaner code instead of .then() chains
  • Include CSRF tokens when making POST requests to Django
  • Build a reusable API client to avoid repetition
  • Handle loading states for better UX
Related Tutorials
Ready to Build?

Skip the boilerplate. Get production-ready Django packages.

Browse Products