HTTP Requests with Fetch API
The Fetch API is the modern way to send asynchronous HTTP requests in JavaScript. It provides a powerful and flexible feature set for making requests and handling responses.
Basic Fetch 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
The more modern and readable approach:
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 );
}
}
Aborting Fetch Requests
Aborting a fetch request in JavaScript is a common need. The signal option allows you to cancel requests at any time.
Using AbortController
To create a valid value for the signal option, you can use AbortController.signal after creating a new instance of AbortController. Then, you can use AbortController.abort() to cancel the request at any time.
// Create the AbortController
const controller = new AbortController ();
const { signal } = controller ;
// Perform the request
fetch ( 'https://my.site.com/data' , { signal })
. then ( res => console . log ( res ))
. catch ( err => {
if ( err . name === 'AbortError' ) {
console . log ( 'Request aborted' );
} else {
console . error ( 'Error:' , err );
}
});
// Abort the request
controller . abort ();
This is particularly useful in scenarios where a request takes too long or the response is no longer needed, such as when a user navigates away from a page.
Timeout Pattern
Combine AbortController with a timeout:
const fetchWithTimeout = ( url , timeout = 5000 ) => {
const controller = new AbortController ();
const { signal } = controller ;
const timeoutId = setTimeout (() => controller . abort (), timeout );
return fetch ( url , { signal })
. then ( response => {
clearTimeout ( timeoutId );
return response ;
})
. catch ( error => {
clearTimeout ( timeoutId );
throw error ;
});
};
// Usage
fetchWithTimeout ( 'https://api.example.com/data' , 3000 )
. then ( response => response . json ())
. then ( data => console . log ( data ))
. catch ( error => console . error ( 'Request failed or timed out:' , error ));
Request Options
GET Request
const response = await fetch ( 'https://api.example.com/users' );
const users = await response . json ();
POST Request
const response = await fetch ( 'https://api.example.com/users' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
name: 'Alice' ,
email: '[email protected] '
})
});
const newUser = await response . json ();
PUT Request
const response = await fetch ( 'https://api.example.com/users/123' , {
method: 'PUT' ,
headers: {
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
name: 'Alice Updated' ,
email: '[email protected] '
})
});
const updatedUser = await response . json ();
DELETE Request
const response = await fetch ( 'https://api.example.com/users/123' , {
method: 'DELETE'
});
if ( response . ok ) {
console . log ( 'User deleted successfully' );
}
Handling Responses
Check Response Status
const response = await fetch ( 'https://api.example.com/data' );
if ( ! response . ok ) {
throw new Error ( `HTTP error! status: ${ response . status } ` );
}
const data = await response . json ();
Different Response Types
// JSON
const jsonData = await response . json ();
// Text
const textData = await response . text ();
// Blob (for files)
const blobData = await response . blob ();
// ArrayBuffer
const bufferData = await response . arrayBuffer ();
// FormData
const formData = await response . formData ();
Error Handling
Comprehensive Error Handling
async function fetchData ( url ) {
try {
const response = await fetch ( url );
// Check if response is ok (status 200-299)
if ( ! response . ok ) {
throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` );
}
const data = await response . json ();
return data ;
} catch ( error ) {
// Network error or abort error
if ( error . name === 'AbortError' ) {
console . log ( 'Request was aborted' );
} else if ( error instanceof TypeError ) {
console . error ( 'Network error:' , error );
} else {
console . error ( 'Error:' , error . message );
}
throw error ;
}
}
Advanced Patterns
Retry Logic
const fetchWithRetry = async ( url , options = {}, retries = 3 ) => {
try {
return await fetch ( url , options );
} catch ( error ) {
if ( retries === 0 ) throw error ;
console . log ( `Retrying... ( ${ retries } attempts left)` );
await new Promise ( resolve => setTimeout ( resolve , 1000 ));
return fetchWithRetry ( url , options , retries - 1 );
}
};
// Usage
fetchWithRetry ( 'https://api.example.com/data' , {}, 3 )
. then ( response => response . json ())
. then ( data => console . log ( data ))
. catch ( error => console . error ( 'Failed after retries:' , error ));
Caching Responses
const cache = new Map ();
const fetchWithCache = async ( url , ttl = 60000 ) => {
const cached = cache . get ( url );
if ( cached && Date . now () - cached . timestamp < ttl ) {
return cached . data ;
}
const response = await fetch ( url );
const data = await response . json ();
cache . set ( url , {
data ,
timestamp: Date . now ()
});
return data ;
};
// Usage
const data = await fetchWithCache ( 'https://api.example.com/data' , 30000 );
Parallel Requests
const [ users , posts , comments ] = await Promise . all ([
fetch ( 'https://api.example.com/users' ). then ( r => r . json ()),
fetch ( 'https://api.example.com/posts' ). then ( r => r . json ()),
fetch ( 'https://api.example.com/comments' ). then ( r => r . json ())
]);
Sequential Requests
const fetchSequentially = async ( urls ) => {
const results = [];
for ( const url of urls ) {
const response = await fetch ( url );
const data = await response . json ();
results . push ( data );
}
return results ;
};
// Usage
const urls = [
'https://api.example.com/users' ,
'https://api.example.com/posts' ,
'https://api.example.com/comments'
];
const results = await fetchSequentially ( urls );
Best Practices
Network requests can fail for many reasons. Always use try/catch or .catch() to handle errors gracefully.
The fetch API doesn’t reject on HTTP errors (like 404 or 500). Always check response.ok before processing the response.
Use AbortController for cleanup
In React components or when navigation occurs, use AbortController to cancel pending requests and prevent memory leaks.
Don’t let requests hang indefinitely. Implement timeout logic to improve user experience.
For frequently accessed data that doesn’t change often, implement caching to reduce network requests.