How to Use Axios in JavaScript: Complete Guide

Axios is a popular HTTP client that simplifies API requests with automatic JSON parsing, request cancellation, interceptors, and cleaner error handling than fetch. Learn when and how to use it.

8 min read

Axios is a promise-based HTTP client for browsers and Node.js. It wraps XMLHttpRequest in browsers, or the http module in Node.js, behind a clean API that handles JSON parsing, error status codes, request timeouts, and interceptors out of the box.

Compared to the Fetch API, Axios removes several friction points: you do not need to call .json() manually, HTTP errors reject the promise automatically, and you can set a timeout without wiring up an AbortController.

javascriptjavascript
// Install: npm install axios
import axios from "axios";
 
async function getUser(id) {
  try {
    const response = await axios.get(`https://api.example.com/users/${id}`);
    console.log(response.data.name);
    return response.data;
  } catch (error) {
    console.error("Failed:", error.message);
  }
}

Three things to notice: the data is already parsed (no .json() call), a 404 or 500 rejects the promise (no response.ok check), and the response object has a consistent shape.

The Axios response object

Every Axios request resolves to a response object with a standard structure, regardless of the HTTP method or response type. That consistency is one of the things fetch does not give you, since a fetch Response only exposes the parsed body after you call a separate method like .json().

javascriptjavascript
const response = await axios.get("/api/users/1");
 
console.log(response.data);       // The parsed response body (JSON, text, etc.)
console.log(response.status);     // HTTP status code: 200, 404, 500
console.log(response.statusText); // "OK", "Not Found", etc.
console.log(response.headers);    // Response headers object
console.log(response.config);     // The request config that was used

The five properties break down simply: data is the parsed body, status and statusText describe the HTTP result, headers is a plain object of response headers, and config is the request configuration that produced this response, useful for logging which URL and method a response came from.

Making GET, POST, PUT, and DELETE requests

Axios provides shorthand methods for every HTTP verb. A plain get call takes the URL, and the params option builds a query string for you instead of concatenating it by hand.

javascriptjavascript
const users = await axios.get("/api/users");
 
const results = await axios.get("/api/search", {
  params: { q: "javascript", page: 1 },
});
// Requests: /api/search?q=javascript&page=1

Creating a resource with post takes the body as the second argument. Axios sets the Content-Type header to application/json and serializes the object for you, so there is no manual stringify step like there is with fetch.

javascriptjavascript
const newUser = await axios.post("/api/users", {
  name: "Taylor",
  email: "taylor@example.com",
});

The remaining verbs follow the same shape: the URL first, then the body. put replaces the whole resource, patch updates only the fields you send, and delete takes no body at all.

javascriptjavascript
await axios.put(`/api/users/${id}`, { name: "Taylor", role: "admin" });
await axios.patch(`/api/users/${id}`, { email: "new@example.com" });
await axios.delete(`/api/users/${id}`);

Creating an Axios instance with defaults

If every request shares the same base URL, headers, or timeout, create an instance instead of configuring every call.

javascriptjavascript
const api = axios.create({
  baseURL: "https://api.example.com",
  timeout: 10000,
  headers: {
    "Content-Type": "application/json",
    "X-API-Version": "v2",
  },
});
 
// Now every call uses these defaults
const user = await api.get("/users/1");
const posts = await api.get("/posts");
await api.post("/users", { name: "Taylor" });

An instance is a pre-configured copy of Axios. You can create multiple instances for different APIs or services, for example one for your own backend with an auth header and one for a third-party API with a different base URL, without either instance's defaults leaking into the other.

Interceptors: transforming requests and responses globally

Interceptors run on every request before it is sent and every response before it reaches your code. They are Axios's most powerful feature, since they let you handle cross-cutting concerns once instead of repeating them at every call site. A request interceptor is the natural place to attach an auth token, since every outgoing call passes through it before the network call actually happens.

javascriptjavascript
const api = axios.create({ baseURL: "https://api.example.com" });
 
api.interceptors.request.use((config) => {
  const token = localStorage.getItem("authToken");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

A response interceptor works the other direction: it sees every response, or every rejected error, before your own code does. Handling a 401 here means every part of the app gets redirected to login the same way, instead of each API call implementing its own redirect logic.

javascriptjavascript
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem("authToken");
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);

Nothing stops you from registering more than one response interceptor on the same instance. A second one, for example, could log every request's method, URL, and status without touching the 401-handling logic in the first, keeping each interceptor focused on a single concern.

javascriptjavascript
api.interceptors.response.use(
  (response) => {
    console.log(`[${response.config.method.toUpperCase()}] ${response.config.url}${response.status}`);
    return response;
  },
  (error) => {
    console.error(`[${error.config?.method?.toUpperCase()}] ${error.config?.url}${error.message}`);
    return Promise.reject(error);
  }
);

Handling errors with Axios

Axios rejects on any HTTP status outside 2xx. The error object has a consistent shape, so one helper can classify any Axios failure regardless of what caused it.

javascriptjavascript
function describeAxiosError(error) {
  if (error.response) return `Status ${error.response.status}: ${JSON.stringify(error.response.data)}`;
  if (error.request) return `No response: ${error.message}`;
  return `Setup error: ${error.message}`;
}

The three branches correspond to three different failure points: the server responded with an error status, the request went out but nothing came back, or the request never made it out at all.

javascriptjavascript
try {
  const response = await axios.get("/api/users/999");
} catch (error) {
  console.log(describeAxiosError(error));
}
PropertyWhen availableMeaning
responseServer responded with errorContains status, data, headers
requestNo response receivedThe request instance (network down, timeout)
messageAlwaysHuman-readable error description

This is simpler than fetch, where you must check response.ok yourself. With Axios, any non-2xx status is already a rejection.

Request cancellation with AbortController

Axios supports cancellation via AbortController, just like fetch. Pass the signal in the request config, and check axios.isCancel(error) in the catch block to tell a deliberate cancellation apart from a real failure.

javascriptjavascript
async function fetchWithCancel(url, controller) {
  try {
    const response = await axios.get(url, { signal: controller.signal });
    return response.data;
  } catch (error) {
    if (axios.isCancel(error)) return null;
    throw error;
  }
}

Wiring a timeout-based cancellation to it looks the same as it does with fetch: create the controller, start the request, and call abort on a timer.

javascriptjavascript
const controller = new AbortController();
setTimeout(() => controller.abort(), 2000);
 
const data = await fetchWithCancel("/api/slow-endpoint", controller);

This works the same way as checking whether error.name equals AbortError with fetch, just with an Axios-specific helper instead of reading the error's name directly.

Request and response timeout

Axios has a built-in timeout option. It rejects with an error if the server does not respond within the specified milliseconds.

javascriptjavascript
try {
  const response = await axios.get("/api/slow-endpoint", {
    timeout: 5000,
  });
} catch (error) {
  if (error.code === "ECONNABORTED") {
    console.error("Request timed out");
  }
}

The error.code for a timeout is "ECONNABORTED". This is specific to Axios and different from the AbortError name used by fetch cancellation.

Uploading files with Axios

Axios supports FormData for file uploads. Optionally track upload progress with the onUploadProgress option.

javascriptjavascript
async function uploadFile(file, onProgress) {
  const formData = new FormData();
  formData.append("file", file);
 
  const response = await axios.post("/api/upload", formData, {
    headers: { "Content-Type": "multipart/form-data" },
    onUploadProgress: (progressEvent) => {
      const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
      onProgress(percent);
    },
  });
 
  return response.data;
}

Unlike fetch, Axios provides upload progress natively. This is the main reason teams choose Axios for file upload features.

Axios vs Fetch: when to use which

AxiosFetch
JSON parsingAutomaticManual, call response.json()
HTTP error rejectionYes (non-2xx rejects)No, must check response.ok
Upload progressBuilt-in, onUploadProgress optionNot directly supported
InterceptorsBuilt-inMust build your own wrapper
Request timeoutBuilt-in, timeout optionNeeds AbortController
Bundle size~14 KB gzipped (dependency)0 KB (built into the platform)
Runtime supportAny modern browser or Node.js versionModern browsers, Node 18+

Choose Axios when you want fewer lines of boilerplate, need interceptors for cross-cutting concerns, or need upload progress. Stick with fetch when you want zero dependencies and your needs are straightforward.

Many teams outgrow a bare fetch setup once they have more than a handful of API calls scattered across the app, since that is when the repeated .json() calls, response.ok checks, and ad-hoc timeout logic start to add up. Reaching for Axios at that point, or building the equivalent wrapper functions around fetch as shown in the linked guides below, solves the same problem either way.

For the fetch-based equivalents of these patterns, see How to Use the JS Fetch API: Complete Tutorial and Handling Post Requests with JS Fetch API Guide. For request cancellation with fetch, see Canceling Fetch Requests in JavaScript: Full Guide.

Rune AI

Rune AI

Key Insights

  • Axios automatically parses JSON responses and rejects on HTTP errors by default.
  • Interceptors let you transform requests and responses globally for auth, logging, and error handling.
  • Create an Axios instance with a base URL and default headers to avoid repetition.
  • Use AbortController with the signal option to cancel requests.
  • Axios and fetch serve the same purpose. Choose based on your boilerplate tolerance and feature needs.
RunePowered by Rune AI

Frequently Asked Questions

Why use Axios instead of fetch?

Axios automatically parses JSON, rejects on HTTP errors by default, has built-in request cancellation, supports interceptors for request/response transformation, and works in older browsers without a polyfill. fetch is built-in and requires no dependency.

Does Axios work in Node.js?

Yes. Axios works in both browsers and Node.js with the same API. In Node.js it uses the http module; in browsers it uses XMLHttpRequest.

How do I cancel an Axios request?

Use an AbortController (same as fetch) and pass the signal in the request config. Axios also supports its own CancelToken, but AbortController is the modern standard.

Conclusion

Axios removes the friction points of fetch: it parses JSON automatically, treats HTTP errors as rejections, and gives you interceptors for cross-cutting concerns like auth tokens and error logging. If your project already uses fetch effectively, stick with it. If you want fewer lines of boilerplate per request, Axios is a solid choice.