Skip to content

AJAX Request

The modern standard for making asynchronous HTTP requests in browsers is the Fetch API. It provides a more powerful and flexible interface than the older XMLHttpRequest.

The simplest use of fetch() involves just providing the URL of the resource you want to retrieve. By default, this performs a GET request.

fetch('https://api.example.com/users') // URL of the resource
.then(response => {
// Check if the response was successful (status code 200-299)
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json(); // Parse the response body as JSON
})
.then(data => {
console.log('Data received:', data);
// Process the received data
})
.catch(error => {
console.error('Fetch error:', error);
// Handle errors (network issues, parsing errors, etc.)
});

fetch() returns a Promise that resolves to the Response object representing the response to the request. You then use methods like .json() or .text() on the Response object to access the actual data, which also return Promises.

To customize the request (e.g., use POST, send data, set headers), pass an options object as the second argument to fetch().

OptionDescription
methodThe HTTP request method (e.g., ‘GET’, ‘POST’, ‘PUT’, ‘DELETE’, ‘PATCH’). Defaults to ‘GET’.
headersAn object or Headers instance containing request headers (e.g., {'Content-Type': 'application/json'}).
bodyThe request body. Can be a string, Blob, BufferSource, FormData, URLSearchParams, or ReadableStream. Must match the Content-Type header.
modeControls whether cross-origin requests are allowed (e.g., ‘cors’, ‘no-cors’, ‘same-origin’). Defaults to ‘cors’.
cacheControls how the request interacts with the browser’s HTTP cache (e.g., ‘default’, ‘no-store’, ‘reload’).
credentialsControls whether cookies are sent with cross-origin requests (‘include’, ‘same-origin’, ‘omit’).

Choosing between GET and POST depends on the action you want to perform:

  • GET: Use for retrieving data. Requests can be cached, bookmarked, and remain in browser history. Data is sent in the URL.
  • POST: Use for submitting data that creates or updates a resource. Requests are not cached by default, don’t remain in browser history, and have no restrictions on data length. Data is sent in the request body.

Always use POST when:

  • The request modifies data on the server (creating, updating, deleting).
  • Sending sensitive information (like passwords).
  • Sending large amounts of data.
  • Sending binary data.

To send data with GET, append it to the URL as query parameters. You can use URLSearchParams to construct the query string safely.

const params = new URLSearchParams({
userId: '123',
limit: 10
});
fetch(`https://api.example.com/posts?${params}`)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

Adding a random parameter (?_=${Date.now()}) is generally not the modern way to prevent caching. Use appropriate Cache-Control headers on the server response and the cache option in the fetch request if needed.

To send JSON data using POST, stringify your JavaScript object and set the Content-Type header to application/json.

const userData = {
name: 'John Doe',
email: 'john.doe@example.com'
};
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
// Add other headers like Authorization if needed
// 'Authorization': 'Bearer YOUR_API_TOKEN'
},
body: JSON.stringify(userData) // Convert JS object to JSON string
})
.then(response => {
if (!response.ok) {
// If server returns error status, parse potential JSON error message
return response.json().then(errData => {
throw new Error(errData.message || `HTTP error! Status: ${response.status}`);
});
}
// Check if response has content before parsing JSON
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return response.json(); // Successfully created, maybe returns new user object
} else {
return {}; // Or handle non-JSON success response (e.g., status 201/204)
}
})
.then(data => {
console.log('Success:', data);
// Handle successful creation (e.g., display success message)
})
.catch(error => {
console.error('Error posting data:', error);
// Display error message to the user
});

You can easily send data from an HTML form using the FormData object. Fetch automatically sets the correct Content-Type (multipart/form-data) when using FormData.

const myForm = document.getElementById('myFormElement');
myForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent default form submission
const formData = new FormData(myForm);
// You can append extra data if needed:
// formData.append('extraField', 'extraValue');
fetch('/submit-form', {
method: 'POST',
body: formData
// No 'Content-Type' header needed; browser sets it for FormData
})
.then(response => response.json()) // Assuming server responds with JSON
.then(data => console.log('Form submission successful:', data))
.catch(error => console.error('Form submission error:', error));
});

The first argument to fetch is typically the URL of a server endpoint (API). This endpoint is designed to receive the request, process it (e.g., interact with a database, perform calculations), and send back a response.

This endpoint can be hosted on the same server as your web page or on a different server (requiring proper CORS configuration).

Fetch is inherently asynchronous and Promise-based. This means:

  • JavaScript execution does not wait for the request to complete.
  • The browser remains responsive, allowing users to interact with the page.
  • You handle the response when the Promise resolves (using .then() or await).

Synchronous requests (XMLHttpRequest with async=false) should not be used. They block the browser’s main thread, leading to a frozen user interface and a terrible user experience. Modern web development relies exclusively on asynchronous patterns.