Axios vs Fetch: A Deep Dive into Modern HTTP Request Handling in JavaScript
Making HTTP requests is a fundamental part of modern web development, enabling communication between the client (like a web browser) and a server. JavaScript provides several ways to achieve this, with the native Fetch API
and the popular third-party library Axios
being two of the most common choices.
This article provides an in-depth look at both Fetch
and Axios
, exploring their features, syntax, pros, cons, and key differences. You'll also find practical examples and guidance on which tool is better suited for different scenarios.
1. The Fetch API
The Fetch API is a modern, powerful, and flexible interface built into most modern web browsers (and available in Node.js via packages like node-fetch
). It provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses, using Promises for handling asynchronous operations.
Core Concepts
-
Promises: Fetch returns a Promise that resolves to the
Response
object, representing the result of the request. - Request and Response Objects: Fine-grained control over the request/response, including headers, status, and body.
- Headers Object: Allows setting and reading HTTP headers.
-
Body Mixin: Shared functionality in both
Request
andResponse
objects, including methods like.json()
,.text()
,.blob()
, etc., which return Promises.
Real Use Cases for Body Mixin
1. Inspecting or Cloning Request Bodies
const originalRequest = new Request('/api/submit', {
method: 'POST',
body: JSON.stringify({ name: 'Ali' }),
headers: { 'Content-Type': 'application/json' }
});
const copy = originalRequest.clone();
copy.text().then(console.log); // Logs: {"name":"Ali"}
2. Reading Request Body in Middleware (e.g., Service Worker)
self.addEventListener('fetch', (event) => {
const req = event.request.clone();
req.json().then(data => {
console.log('Intercepted request body:', data);
});
});
2. Basic Usage of Fetch
GET Request
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))
.catch(error => console.error('Fetch error:', error));
POST Request
const userData = { name: 'John Doe', email: '[email protected]' };
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN'
},
body: JSON.stringify(userData)
})
.then(response => {
if (!response.ok) {
throw new Error(\`HTTP error! status: \${response.status}\`);
}
return response.json();
})
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));
3. Error Handling in Fetch
- Network Errors: Only network errors (e.g., no internet) cause the Promise to reject.
-
HTTP Errors (404, 500, etc.): These don’t reject the Promise. You need to manually check
response.ok
.
4. Request Cancellation with AbortController
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 aborted');
} else {
console.error('Fetch error:', err);
}
});
controller.abort();
More on AbortController: here
5. Browser Compatibility
Fetch is supported in all modern browsers (Chrome, Firefox, Safari, Edge). Polyfills are required for Internet Explorer.
6. Pros and Cons of Fetch
Pros
- Native: No need to install anything; built into modern browsers.
- Flexible: Full control over low-level HTTP features.
-
Promise-based: Works well with
async/await
.
Cons
- Verbose Error Handling
- JSON Handling Requires Two Steps
- No Built-in Interceptors
const originalFetch = window.fetch;
window.fetch = async (...args) => {
console.log('Fetching:', args[0]);
const response = await originalFetch(...args);
console.log('Response:', response);
return response;
};
- No Built-in Timeout
function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
return fetch(url, { signal: controller.signal })
.finally(() => clearTimeout(timer));
}
Alternate using Promise.race:
function fetchWithTimeout(url, timeout = 5000) {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timed out')), timeout)
);
return Promise.race([
fetch(url),
timeoutPromise
]);
}
- No Built-in XSRF Protection
More on XSRF: here
- No Upload/Download Progress Tracking
Use XMLHttpRequest
:
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(\`Upload: \${percent.toFixed(2)}%\`);
}
};
xhr.open('POST', '/upload');
xhr.send(file);
Conclusion
While the Fetch API is modern and flexible, it lacks features like request/response interceptors, progress tracking, and built-in timeout/XSRF protection. For simple use cases, it's perfect. But for advanced needs like upload tracking, retry logic, or global error handling, a library like Axios may be more appropriate.
In part 2 of this series, we’ll cover Axios, compare it head-to-head with Fetch, and help you decide which one fits your project best.
2. Axios
Axios is a popular, feature-rich, Promise-based HTTP client library that works in both the browser and Node.js environments. It simplifies many common tasks associated with making HTTP requests.
Core Features
- Promise-based: Uses Promises, compatible with async/await.
- Automatic JSON Transformation: Automatically stringifies request data and parses response data to JSON.
- Request and Response Interceptors: Allows you to intercept requests or responses before they are handled by then or catch. Useful for logging, adding headers, or handling errors globally.
- Error Handling: Throws errors for bad HTTP statuses (4xx, 5xx), simplifying error handling.
- Request Cancellation: Supports request cancellation using a cancel token (similar to AbortController).
- Client-side XSRF Protection: Offers built-in support.
- Timeout Configuration: Easy to set request timeouts.
- Wider Browser Compatibility: Handles browser inconsistencies and provides support for older browsers (using XMLHttpRequest internally).
- Upload/Download Progress: Provides progress tracking capabilities.
Basic Usage (GET Request)
import axios from 'axios'; // Or use a CDN link in HTML
axios.get('https://api.example.com/users')
.then(response => {
// Data is directly available in response.data
console.log(response.data);
console.log('Status:', response.status);
})
.catch(error => {
// Handles both network errors and HTTP errors (4xx, 5xx)
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error('Error data:', error.response.data);
console.error('Error status:', error.response.status);
console.error('Error headers:', error.response.headers);
} else if (error.request) {
// The request was made but no response was received
console.error('Error request:', error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error message:', error.message);
}
console.error('Error config:', error.config);
});
Making Other Requests (POST, PUT, DELETE)
Axios provides convenience methods (axios.post, axios.put, axios.delete) and a general axios(config) method.
// POST Request Example
const userData = { name: 'Jane Doe', email: '[email protected]' };
axios.post('https://api.example.com/users', userData, {
headers: {
'Authorization': 'Bearer YOUR_TOKEN'
},
timeout: 5000 // Example: Set timeout to 5 seconds
})
.then(response => {
console.log('Success:', response.data);
})
.catch(error => {
console.error('Error:', error.message);
// Detailed error handling as shown in the GET example
});
// Alternative using the config object
axios({
method: 'put',
url: 'https://api.example.com/users/1',
data: { name: 'Jane Smith' }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
Interceptors
Interceptors allow you to run code globally before requests are sent or before responses are processed.
// Add a request interceptor
axios.interceptors.request.use(config => {
// Do something before request is sent (e.g., add auth token)
console.log('Sending request to:', config.url);
const token = localStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(response => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
console.log('Received response status:', response.status);
return response;
}, error => {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error (e.g., global error handling)
if (error.response && error.response.status === 401) {
console.error('Unauthorized! Redirecting to login...');
// window.location.href = '/login';
}
return Promise.reject(error);
});
// Now any axios request will use these interceptors
axios.get('/some-protected-resource')
.then(response => {/* ... */});
Error Handling
Axios automatically rejects the promise for status codes outside the 2xx range. The error object in the .catch block provides detailed information in error.response, error.request, or error.message.
Request Cancellation
Axios uses a CancelToken source.
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('https://api.example.com/data', {
cancelToken: source.token
})
.then(response => console.log(response.data))
.catch(thrown => {
if (axios.isCancel(thrown)) {
console.log('Request canceled:', thrown.message);
} else {
// handle error
console.error('Axios error:', thrown);
}
});
// Cancel the request (example: request is cancelled after 1 second)
// setTimeout(() => {
// source.cancel('Operation canceled by the user.');
// }, 1000);
Pros of Axios
- Ease of Use: Simpler API for common tasks like JSON handling and error checking.
- Interceptors: Powerful mechanism for global request/response manipulation.
- Automatic JSON Handling: Reduces boilerplate code.
- Better Error Handling: Rejects promises on HTTP error statuses (4xx, 5xx) by default.
- Built-in XSRF Protection: Provides client-side support.
- Timeout Configuration: Simple option to set request timeouts.
- Upload/Download Progress: Built-in support.
- Wider Browser Compatibility: Works in older browsers without requiring manual polyfills for basic functionality.
- Node.js Support: Works seamlessly in both browser and Node.js environments.
Cons of Axios
- External Library: Adds a dependency to your project, increasing bundle size (though often negligibly small).
- Slightly Less "Standard": While popular, it's not a built-in browser standard like Fetch.
3. Fetch vs. Axios: Key Differences Summarized
Feature | Fetch API | Axios |
---|---|---|
Source | Built-in browser API (requires polyfill for old browsers) | Third-party library (install via npm/yarn or CDN) |
Promise Rejection | Only on network error. HTTP errors (4xx, 5xx) resolve successfully. Requires manual check. | On network error AND HTTP errors (4xx, 5xx) by default. |
JSON Handling | Two-step process: fetch().then(res => res.json())
|
Automatic request/response transformation. Data in response.data . |
Request Data | Body needs manual stringification (e.g., JSON.stringify() ). |
Automatically stringifies data (if object). |
Headers | Use Headers object or plain object. Content-Type often needs manual setting. |
Set via headers config. Often sets Content-Type automatically. |
Interceptors | No built-in support. | Yes, for both request and response. |
Timeout | No built-in support. Requires AbortController + setTimeout . |
Built-in timeout config option. |
Cancellation | Uses AbortController . |
Uses CancelToken (older API) or AbortController (newer versions). |
XSRF Protection | Manual implementation required. | Built-in client-side support. |
Progress Tracking | No built-in support for upload/download. | Built-in support for upload/download progress. |
Browser Support | Modern browsers. Requires polyfills for older ones (IE). | Wider compatibility, uses XMLHttpRequest internally. |
Node.js Support | Requires node-fetch or similar package. |
Works out-of-the-box. |
Bundle Size | None (if native support used). | Adds library size to the bundle. |
In short, choose Fetch
if you prefer a built-in browser standard and want minimal dependencies, especially for simpler tasks. Choose Axios if you need more features like interceptors, easier error handling, timeouts, and better compatibility, especially for larger projects.