Axios JavaScript: The Complete Guide to HTTP Requests in Modern Web Development

Web Development

What is Axios?

Axios is a promise-based HTTP client library for JavaScript that works seamlessly in both browser and Node.js environments. Unlike the native Fetch API, Axios provides a comprehensive feature set out of the box, making it the preferred choice for developers building modern web applications.

Cross-platform compatibility, automatic JSON transformation, and a rich feature set including request interceptors and automatic CSRF protection make Axios indispensable for enterprise-grade applications.

For development teams building with Next.js, React, or Node.js, Axios provides consistent patterns that scale from prototype to production without requiring architectural changes.

Axios by the Numbers

48M+

Weekly Downloads

105K+

GitHub Stars

IE11+

Browser Support

100%

Promise-Based

Installation and Setup

Getting started with Axios is straightforward. Whether you're working with a modern bundler or need a quick script tag solution, Axios has you covered.

Package Manager Installation
# npm
npm install axios

# yarn
yarn add axios

# pnpm
pnpm add axios
CDN Script Tag
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
ES Module Import
import axios from 'axios';

// For tree-shaking optimized bundles
import axios from 'axios/index.mjs';

Making HTTP Requests

Axios provides intuitive methods for all HTTP operations. Each method returns a Promise, enabling clean async/await syntax throughout your application.

GET Requests

GET requests retrieve data from a server. Axios simplifies query parameter handling and header configuration.

GET Request Examples
// Simple GET request
const response = await axios.get('https://api.example.com/users');

// GET with query parameters
const users = await axios.get('https://api.example.com/users', {
 params: {
 page: 1,
 limit: 50,
 sort: 'createdAt'
 }
});

// GET with custom headers
const privateData = await axios.get('https://api.example.com/private', {
 headers: {
 'Authorization': `Bearer ${token}`,
 'Accept': 'application/json'
 }
});

POST Requests

POST requests send data to the server for creation. Axios handles JSON and form data seamlessly.

POST Request Examples
// POST JSON data
const newUser = await axios.post('https://api.example.com/users', {
 name: 'John Doe',
 email: '[email protected]',
 role: 'developer'
});

// POST with form data
const formData = new FormData();
formData.append('name', 'John Doe');
formData.append('avatar', fileInput.files[0]);

const response = await axios.post('https://api.example.com/users', formData, {
 headers: {
 'Content-Type': 'multipart/form-data'
 }
});

PUT and PATCH Requests

PUT replaces entire resources while PATCH performs partial updates. Understanding the distinction is crucial for API design.

PUT vs PATCH Comparison
// PUT - Complete resource replacement
const updatedUser = await axios.put('https://api.example.com/users/123', {
 name: 'Jane Doe',
 email: '[email protected]',
 role: 'admin'
});

// PATCH - Partial update
const patchedUser = await axios.patch('https://api.example.com/users/123', {
 email: '[email protected]' // Only update email
});

DELETE Requests

DELETE requests remove resources from the server. Axios also supports request bodies when APIs require them.

DELETE Request Examples
// Simple DELETE
await axios.delete('https://api.example.com/users/123');

// DELETE with request body
await axios.delete('https://api.example.com/users/123', {
 data: {
 reason: 'user_request',
 confirm: true
 }
});

Understanding Responses

Every Axios response contains a rich object with multiple properties for comprehensive data access. This structured response eliminates the need for manual JSON parsing that the Fetch API requires.

Response Object Structure
const response = await axios.get('https://api.example.com/users');

// Response body data (auto-parsed JSON)
response.data;

// HTTP status code
response.status;

// Status text
response.statusText; // "OK"

// Response headers
response.headers;

// Request configuration that generated this response
response.config;
Destructuring Responses
// Extract only what you need
const { data: users, status } = await axios.get('https://api.example.com/users');

if (status === 200) {
 console.log(users);
}

Error Handling

Robust error handling is essential for production applications. Axios provides detailed error objects that distinguish between server errors, network failures, and request configuration issues.

Comprehensive Error Handling
try {
 const response = await axios.get('https://api.example.com/protected');
} catch (error) {
 if (error.response) {
 // Server responded with error status (4xx, 5xx)
 console.log('Server Error:', error.response.status);
 console.log('Response Data:', error.response.data);
 } else if (error.request) {
 // Request made but no response received
 console.log('Network Error: No response received');
 } else {
 // Error setting up the request
 console.log('Request Setup Error:', error.message);
 }
}
CodeMeaningHandling Strategy
400Bad RequestValidate input, show user-friendly message
401UnauthorizedRefresh token or redirect to login
403ForbiddenCheck permissions, show access denied
404Not FoundHandle gracefully, show fallback content
500Server ErrorRetry with backoff, notify monitoring

Interceptors: The Power Feature

Interceptors are Axios' most powerful feature for cross-cutting concerns like authentication, logging, and error handling. They allow you to modify requests and responses globally without duplicating code across your application.

Request Interceptor
// Add auth token to every request
axios.interceptors.request.use(
 (config) => {
 const token = localStorage.getItem('authToken');
 if (token) {
 config.headers.Authorization = `Bearer ${token}`;
 }

 // Add request timestamp for performance tracking
 config.metadata = { startTime: Date.now() };

 return config;
 },
 (error) => Promise.reject(error)
);
Response Interceptor with Token Refresh
// Global response handler
axios.interceptors.response.use(
 (response) => {
 // Add response time header
 const duration = Date.now() - response.config.metadata.startTime;
 console.log(`${response.config.url}: ${duration}ms`);

 return response;
 },
 async (error) => {
 // Handle 401 globally - attempt token refresh
 if (error.response?.status === 401 && !error.config._retry) {
 error.config._retry = true;

 try {
 const refreshToken = localStorage.getItem('refreshToken');
 const { data } = await axios.post('/api/refresh-token', {
 refreshToken
 });

 localStorage.setItem('authToken', data.accessToken);
 error.config.headers.Authorization = `Bearer ${data.accessToken}`;

 return axios(error.config);
 } catch (refreshError) {
 window.location.href = '/login';
 return Promise.reject(refreshError);
 }
 }

 return Promise.reject(error);
 }
);

Axios Instances

Creating reusable Axios instances with custom configurations is essential for applications that interact with multiple APIs. Each instance maintains its own interceptors and default settings.

Creating Axios Instances
// API instance for main backend
const api = axios.create({
 baseURL: 'https://api.example.com',
 timeout: 10000,
 headers: {
 'Content-Type': 'application/json'
 }
});

// Analytics instance
const analytics = axios.create({
 baseURL: 'https://analytics.example.com',
 timeout: 5000,
 headers: {
 'X-Analytics-Key': process.env.ANALYTICS_KEY
 }
});

// Apply interceptors to specific instances
api.interceptors.request.use(authInterceptor);
analytics.interceptors.request.use(timingInterceptor);

Request Cancellation

Proper request cancellation prevents memory leaks and reduces unnecessary network traffic. This is especially important in React components where users may navigate away before requests complete.

Cancel Token Example
import axios from 'axios';

// Create cancel token source
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

// Make cancellable request
const request = axios.get('/api/users', {
 cancelToken: source.token
});

// Cancel the request
source.cancel('Operation cancelled by user');

// Check if request was cancelled
if (axios.isCancel(error)) {
 console.log('Request cancelled:', error.message);
}
AbortController Integration
// Modern approach using AbortController
const controller = new AbortController();
const signal = controller.signal;

const response = await axios.get('/api/users', { signal });

// Abort after timeout
setTimeout(() => controller.abort(), 5000);
React Component Integration
useEffect(() => {
 const controller = new AbortController();

 const fetchData = async () => {
 try {
 const response = await axios.get('/api/data', {
 signal: controller.signal
 });
 setData(response.data);
 } catch (error) {
 if (!axios.isCancel(error)) {
 setError(error);
 }
 }
 };

 fetchData();

 // Cleanup: cancel on unmount
 return () => controller.abort();
}, []);

Progress Tracking

For file uploads and large data transfers, Axios provides built-in progress callbacks that enable real-time progress indicators in your user interface.

Upload and Download Progress
const formData = new FormData();
formData.append('file', fileInput.files[0]);

const response = await axios.post('/api/upload', formData, {
 onUploadProgress: (progressEvent) => {
 const percentCompleted = Math.round(
 (progressEvent.loaded * 100) / progressEvent.total
 );
 console.log(`Upload: ${percentCompleted}%`);
 setUploadProgress(percentCompleted);
 },

 onDownloadProgress: (progressEvent) => {
 const percentCompleted = Math.round(
 (progressEvent.loaded * 100) / progressEvent.total
 );
 console.log(`Download: ${percentCompleted}%`);
 }
});

TypeScript Support

Axios includes comprehensive TypeScript definitions out of the box. This enables type-safe API calls, generic response wrappers, and excellent IDE support for your API layer. When combined with proper type definitions and techniques like type casting in TypeScript, you can build robust, maintainable HTTP layers that catch errors at compile time rather than runtime.

Type-Safe API Client
import axios, { AxiosResponse } from 'axios';

interface User {
 id: number;
 name: string;
 email: string;
 createdAt: string;
}

async function getUser(id: number): Promise<User> {
 const response: AxiosResponse<User> = await axios.get(
 `https://api.example.com/users/${id}`
 );
 return response.data;
}

// Generic response wrapper
interface ApiResponse<T> {
 data: T;
 status: number;
 message: string;
}

async function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
 const response = await axios.get<ApiResponse<T>>(url);
 return response.data;
}

Best Practices

Implementing these production-ready patterns ensures maintainable, reliable API interactions that scale with your application.

Request Retry Logic
axios.interceptors.response.use(
 null,
 async (error) => {
 if (error.config && !error.config._retry) {
 error.config._retry = true;

 // Retry on 5xx errors
 if (error.response?.status >= 500) {
 await new Promise(resolve => setTimeout(resolve, 1000));
 return axios(error.config);
 }
 }
 return Promise.reject(error);
 }
);
Environment Configuration
const api = axios.create({
 baseURL: process.env.NEXT_PUBLIC_API_URL,
 timeout: parseInt(process.env.API_TIMEOUT) || 10000
});

Comparison with Fetch API

While the native Fetch API is built into browsers, Axios offers significant advantages for production applications. Understanding both helps you choose the right tool for each scenario.

FeatureAxiosFetch API
JSON transformationAutomaticManual .json() call
Error handlingThrows on 4xx/5xxOnly network errors throw
Request timeoutBuilt-inManual AbortController
Progress eventsBuilt-inLimited support
InterceptorsNativeRequires wrapper
Request cancellationCancelToken + AbortControllerAbortController only
Bundle size~13KB minified0 (native)
Browser supportIE11+Modern browsers
TypeScriptBuilt-in typesRequires polyfill

When to Use Axios

  • Complex applications requiring interceptors
  • Need automatic JSON handling
  • Cross-browser compatibility required
  • TypeScript projects requiring type safety

When to Use Fetch

  • Minimal bundle size critical
  • Modern browser support only
  • Simple one-off requests
  • No dependencies policy

Common Pitfalls and Solutions

Avoid these common mistakes to build more reliable HTTP layers in your applications.

Not Handling Errors

Always wrap requests in try-catch blocks and handle specific error types.

Forgetting to Cancel Requests

Use AbortController in cleanup functions to prevent memory leaks in React components.

Hardcoding URLs

Centralize Axios instances with environment-based configuration.

Ignoring Response Status

Validate response data structure, don't assume 2xx means valid data.

Conclusion

Axios remains the gold standard for HTTP requests in JavaScript applications. Its balance of simplicity and power makes it suitable for everything from rapid prototypes to enterprise applications. By mastering Axios' features--instances, interceptors, cancel tokens, and TypeScript support--you can build robust, maintainable HTTP layers that scale with your application.

The key to success is understanding when to use Axios' advanced features and when to keep things simple. Start with basic requests, add interceptors as cross-cutting concerns emerge, and leverage TypeScript for long-term maintainability. For teams working on web development projects, investing time in mastering Axios pays dividends in code quality and developer productivity.

Need Help with Your Web Development Project?

Our team specializes in building modern web applications with React, Next.js, and TypeScript. We can help you implement robust HTTP layers with Axios and best practices for API integration.

Sources

  1. Axios GitHub Repository - Official repository with 105K+ stars
  2. Generalist Programmer - Axios HTTP Client Complete Guide - Comprehensive tutorial
  3. LogRocket Blog - Axios in JavaScript - Detailed guide with practical examples
  4. Apidog - How to Make HTTP Requests with Axios - Modern 2025 guide
  5. MDN Web Docs - Using Fetch - Native Fetch API reference