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.
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.
// 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().
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 usedThe 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.
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=1Creating 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.
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.
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.
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.
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.
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.
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.
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.
try {
const response = await axios.get("/api/users/999");
} catch (error) {
console.log(describeAxiosError(error));
}| Property | When available | Meaning |
|---|---|---|
| response | Server responded with error | Contains status, data, headers |
| request | No response received | The request instance (network down, timeout) |
| message | Always | Human-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.
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.
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.
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.
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
| Axios | Fetch | |
|---|---|---|
| JSON parsing | Automatic | Manual, call response.json() |
| HTTP error rejection | Yes (non-2xx rejects) | No, must check response.ok |
| Upload progress | Built-in, onUploadProgress option | Not directly supported |
| Interceptors | Built-in | Must build your own wrapper |
| Request timeout | Built-in, timeout option | Needs AbortController |
| Bundle size | ~14 KB gzipped (dependency) | 0 KB (built into the platform) |
| Runtime support | Any modern browser or Node.js version | Modern 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
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.
Frequently Asked Questions
Why use Axios instead of fetch?
Does Axios work in Node.js?
How do I cancel an Axios request?
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.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.