Axios Complete Guide | Promise-Based HTTP Client for JavaScript

Axios Complete Guide | Promise-Based HTTP Client for JavaScript

이 글의 핵심

Axios is a promise-based HTTP client for JavaScript. It works in both browser and Node.js, providing a clean API for making HTTP requests with interceptors, automatic JSON handling, and request cancellation.

Introduction

Axios is a promise-based HTTP client for the browser and Node.js. It provides a simple, clean API for making HTTP requests with features like interceptors, automatic JSON transformation, and request cancellation.

Why Axios?

Native Fetch:

fetch('https://api.example.com/users')
  .then(response => {
    if (!response.ok) throw new Error('HTTP error');
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error(error));

With Axios:

axios.get('https://api.example.com/users')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

1. Installation

# npm
npm install axios

# yarn
yarn add axios

# CDN (browser)
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

2. Basic Usage

GET Request

const axios = require('axios');

// Simple GET
axios.get('https://api.example.com/users')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error(error);
  });

// Async/await
async function getUsers() {
  try {
    const response = await axios.get('https://api.example.com/users');
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
}

// With query parameters
axios.get('https://api.example.com/users', {
  params: {
    page: 1,
    limit: 10,
  }
});
// Request: GET /users?page=1&limit=10

POST Request

// POST with JSON body
axios.post('https://api.example.com/users', {
  name: 'Alice',
  email: 'alice@example.com',
})
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

// With headers
axios.post('https://api.example.com/users', {
  name: 'Alice',
}, {
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123',
  }
});

PUT Request

// Update user
axios.put('https://api.example.com/users/123', {
  name: 'Alice Updated',
  email: 'alice-new@example.com',
})
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

DELETE Request

// Delete user
axios.delete('https://api.example.com/users/123')
  .then(response => console.log('Deleted'))
  .catch(error => console.error(error));

// With data
axios.delete('https://api.example.com/users/123', {
  data: { reason: 'Account closed' }
});

3. Axios Instance

// Create instance with default config
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Use instance
api.get('/users');           // GET https://api.example.com/users
api.post('/users', { ... }); // POST https://api.example.com/users

// Multiple instances
const apiV1 = axios.create({ baseURL: 'https://api.example.com/v1' });
const apiV2 = axios.create({ baseURL: 'https://api.example.com/v2' });

4. Request Configuration

axios({
  method: 'post',
  url: '/users',
  baseURL: 'https://api.example.com',
  headers: {
    'Authorization': 'Bearer token123',
    'Content-Type': 'application/json',
  },
  params: {
    page: 1,
  },
  data: {
    name: 'Alice',
    email: 'alice@example.com',
  },
  timeout: 5000,
  withCredentials: true, // Send cookies
  responseType: 'json',  // 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  maxRedirects: 5,
  validateStatus: (status) => status >= 200 && status < 300,
});

5. Response Schema

const response = await axios.get('/users/123');

console.log(response.data);       // Response body
console.log(response.status);     // 200
console.log(response.statusText); // "OK"
console.log(response.headers);    // Response headers
console.log(response.config);     // Request config
console.log(response.request);    // XMLHttpRequest

6. Error Handling

try {
  const response = await axios.get('/users/123');
  console.log(response.data);
} catch (error) {
  if (error.response) {
    // Server responded with error status
    console.error('Status:', error.response.status);
    console.error('Data:', error.response.data);
    console.error('Headers:', error.response.headers);
  } else if (error.request) {
    // Request made but no response
    console.error('No response:', error.request);
  } else {
    // Error setting up request
    console.error('Error:', error.message);
  }
}

// Custom error handling
axios.get('/users/123')
  .catch(error => {
    if (error.response?.status === 404) {
      console.log('User not found');
    } else if (error.response?.status === 500) {
      console.log('Server error');
    } else {
      console.log('Unknown error');
    }
  });

7. Interceptors

Request Interceptors

// Add request interceptor
axios.interceptors.request.use(
  (config) => {
    // Modify config before request
    console.log('Request:', config.method, config.url);
    
    // Add auth token
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

Response Interceptors

// Add response interceptor
axios.interceptors.response.use(
  (response) => {
    // Transform response data
    console.log('Response:', response.status);
    return response;
  },
  async (error) => {
    // Handle errors globally
    const originalRequest = error.config;
    
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      
      // Refresh token
      const newToken = await refreshAuthToken();
      axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
      
      return axios(originalRequest);
    }
    
    return Promise.reject(error);
  }
);

Remove Interceptors

const interceptor = axios.interceptors.request.use(config => config);

// Remove later
axios.interceptors.request.eject(interceptor);

8. Request Cancellation

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/users', {
  cancelToken: source.token
})
  .catch(error => {
    if (axios.isCancel(error)) {
      console.log('Request canceled:', error.message);
    }
  });

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

// AbortController (modern)
const controller = new AbortController();

axios.get('/users', {
  signal: controller.signal
});

// Cancel
controller.abort();

9. Concurrent Requests

// Multiple requests
const [users, posts, comments] = await Promise.all([
  axios.get('/users'),
  axios.get('/posts'),
  axios.get('/comments'),
]);

console.log(users.data);
console.log(posts.data);
console.log(comments.data);

// With axios.all (deprecated, use Promise.all)
axios.all([
  axios.get('/users'),
  axios.get('/posts'),
])
  .then(axios.spread((users, posts) => {
    console.log(users.data);
    console.log(posts.data);
  }));

10. Form Data

// Multipart form data
const formData = new FormData();
formData.append('name', 'Alice');
formData.append('file', fileInput.files[0]);

axios.post('/upload', formData, {
  headers: {
    'Content-Type': 'multipart/form-data',
  },
  onUploadProgress: (progressEvent) => {
    const percentCompleted = Math.round(
      (progressEvent.loaded * 100) / progressEvent.total
    );
    console.log(`Upload: ${percentCompleted}%`);
  },
});

// URL-encoded form data
const params = new URLSearchParams();
params.append('name', 'Alice');
params.append('email', 'alice@example.com');

axios.post('/users', params, {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
});

11. Real-World Example: API Client

// api.js
import axios from 'axios';

const api = axios.create({
  baseURL: process.env.REACT_APP_API_URL || 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Request interceptor
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// Response interceptor
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    
    // Handle 401 (unauthorized)
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      
      try {
        const { data } = await axios.post('/auth/refresh', {
          refreshToken: localStorage.getItem('refreshToken'),
        });
        
        localStorage.setItem('token', data.token);
        api.defaults.headers.common['Authorization'] = `Bearer ${data.token}`;
        
        return api(originalRequest);
      } catch (refreshError) {
        // Redirect to login
        window.location.href = '/login';
        return Promise.reject(refreshError);
      }
    }
    
    // Handle other errors
    if (error.response?.status === 500) {
      console.error('Server error:', error.response.data);
    }
    
    return Promise.reject(error);
  }
);

// API methods
export const usersAPI = {
  getAll: (params) => api.get('/users', { params }),
  getById: (id) => api.get(`/users/${id}`),
  create: (data) => api.post('/users', data),
  update: (id, data) => api.put(`/users/${id}`, data),
  delete: (id) => api.delete(`/users/${id}`),
};

export const postsAPI = {
  getAll: (params) => api.get('/posts', { params }),
  getById: (id) => api.get(`/posts/${id}`),
  create: (data) => api.post('/posts', data),
  update: (id, data) => api.put(`/posts/${id}`, data),
  delete: (id) => api.delete(`/posts/${id}`),
};

export default api;

12. TypeScript Support

import axios, { AxiosResponse } from 'axios';

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

// Typed GET request
const response: AxiosResponse<User> = await axios.get<User>('/users/123');
const user: User = response.data;

// Typed POST request
const createUser = async (userData: Omit<User, 'id'>): Promise<User> => {
  const response = await axios.post<User>('/users', userData);
  return response.data;
};

// Generic API function
async function fetchData<T>(url: string): Promise<T> {
  const response = await axios.get<T>(url);
  return response.data;
}

const users = await fetchData<User[]>('/users');

13. Best Practices

1. Use Axios Instance

// Good: centralized config
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
});

// Bad: repeated config
axios.get('https://api.example.com/users', { timeout: 10000 });
axios.post('https://api.example.com/users', data, { timeout: 10000 });

2. Global Error Handling

api.interceptors.response.use(
  response => response,
  error => {
    // Log errors
    console.error('API Error:', error);
    
    // Show toast notification
    showErrorToast(error.response?.data?.message);
    
    return Promise.reject(error);
  }
);

3. Request Retry Logic

const axiosRetry = require('axios-retry');

axiosRetry(axios, {
  retries: 3,
  retryDelay: axiosRetry.exponentialDelay,
  retryCondition: (error) => {
    return axiosRetry.isNetworkOrIdempotentRequestError(error) ||
           error.response?.status === 429;
  },
});

4. Timeout Configuration

const api = axios.create({
  timeout: 10000, // 10 seconds
});

// Or per request
axios.get('/users', { timeout: 5000 });

14. Debugging

// Enable debug logging
axios.interceptors.request.use(config => {
  console.log('→', config.method?.toUpperCase(), config.url);
  return config;
});

axios.interceptors.response.use(response => {
  console.log('←', response.status, response.config.url);
  return response;
});

// Log full request/response
axios.get('/users')
  .then(response => {
    console.log('Request:', response.config);
    console.log('Response:', response);
  });

Summary

Axios is the go-to HTTP client for JavaScript:

  • Promise-based API with async/await
  • Interceptors for global request/response handling
  • Automatic JSON transformation
  • Request cancellation support
  • Works everywhere (browser + Node.js)

Key Takeaways:

  1. Use axios instances for configuration
  2. Leverage interceptors for auth and errors
  3. Handle errors with try/catch
  4. Cancel requests when needed
  5. TypeScript for type safety

Next Steps:

  • Build Express API
  • Learn Fetch API
  • Handle JWT Authentication

Resources: