JavaScript Fetch API: Requests and Responses
The Fetch API is a modern, promise-based interface built into JavaScript that allows web browsers to make asynchronous HTTP requests to web servers. This article provides a comprehensive breakdown of how the Fetch API handles network communication, including initiating requests, configuring custom headers and methods, parsing incoming response data, and managing errors effectively.
The Basics of Making a Request
The fetch() method is available on the global
window object. At its simplest, it takes a single mandatory
argument: the URL path to the resource you want to retrieve.
By default, fetch() performs a standard HTTP
GET request:
fetch('https://api.example.com/data')
.then(response => {
// Process the response here
})
.catch(error => {
// Handle network errors here
});Because fetch() is Promise-based, it allows for clean,
non-blocking asynchronous execution using either standard Promise
chaining (.then()) or modern async/await
syntax.
Handling the HTTP Response
When a fetch request completes successfully, the Promise resolves to
a Response object. This object does not immediately contain
the actual body data in readable format; instead, it represents the
entire HTTP response, including metadata.
1. Reading Headers and Status
The Response object provides several properties to
inspect the server’s reply: * response.status: The numeric
HTTP status code (e.g., 200, 404,
500). * response.ok: A boolean that returns
true if the status code is within the 200–299 range. *
response.headers: An interface to inspect response
headers.
2. Parsing the Response Body
To access the payload, you must call the appropriate body-reading
method, which returns another Promise: * response.json():
Parses the response as JSON data. * response.text(): Reads
the response as plain text. * response.blob(): Reads the
response as raw binary data (ideal for images or files). *
response.formData(): Parses multipart form data.
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log(data);
});Sending Custom HTTP Requests (POST, PUT, DELETE)
To send data or use HTTP methods other than GET, pass an
optional configuration object as the second parameter to
fetch().
This configuration object accepts several key options: *
method: The HTTP verb (e.g., 'POST',
'PUT', 'DELETE'). * headers: An
object defining HTTP headers, such as Content-Type or
Authorization. * body: The data payload sent
to the server (must be a string, FormData,
Blob, or BufferSource).
const payload = { username: 'john_doe', role: 'admin' };
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE'
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(result => console.log('Success:', result))
.catch(error => console.error('Error:', error));Handling Errors in Fetch
A crucial distinction in the Fetch API is that a fetch()
Promise does not reject on HTTP error statuses such as
404 Not Found or 500 Internal Server Error.
Instead, the Promise resolves normally, and the response.ok
property is set to false.
The Promise only rejects when a network failure occurs, such as a loss of internet connection, a DNS lookup failure, or a blocked CORS request.
To handle both network failures and server errors, verify the
response.ok property explicitly:
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`Request failed with status: ${response.status}`);
}
const userData = await response.json();
return userData;
} catch (error) {
console.error('Fetch error:', error.message);
}
}Canceling Requests with AbortController
The Fetch API supports request cancellation via the
AbortController interface. This is useful for preventing
stale network requests when user input changes rapidly (such as in
autocomplete search bars).
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch request was canceled.');
} else {
console.error('Other error:', err);
}
});
// Cancel the request
controller.abort();