The first network call of a project fits in three lines. The twentieth drags along the same base address, the same authorization header and the same error handling, copied twenty times over.
Axios exists so that shared part gets written once.
Definition
Axios is an HTTP client library, usable in the browser and under Node.js alike. Every call returns a Promise, the JSON body arrives already parsed, and a failure status rejects the promise instead of fulfilling it.
import axios from "axios";
const api = axios.create({
baseURL: "https://api.example.com",
timeout: 5000,
headers: { "X-Client": "believemy" },
});
const { data } = await api.get("/invoices/42");
console.log(data.amount); // 19The instance carries the base address, the timeout and the shared headers. Every other file writes nothing but the path, and a change of address touches a single place.
What sets it apart from fetch
| Topic | With fetch | With Axios |
|---|---|---|
| 404 status | The promise fulfills, checking is on you | The promise rejects |
| JSON body | An explicit second step | Already parsed in data |
| Timeout | Wired up with an AbortController | A timeout option |
| Shared settings | Written by hand | One shared instance |
| Installation | Nothing, fetch() is built in | A package to add |
The table holds three columns because there are only three questions to ask: the topic, then what each of the two clients does about it. The first row is by far the most surprising one, a failure status going unnoticed with fetch whenever nobody tests response.ok.
Interceptors
An interceptor is a function called on every request or every response of the instance. That is where the authentication token, the logging trace or the shared reaction to an expired session belong.
api.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${readToken()}`;
return config;
});
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) signOut();
return Promise.reject(error);
}
);The Optional chaining (?.) on error.response is not decorative: when the server does not answer at all, the property is absent and a direct read would raise a TypeError on top of the network error.
Frequently asked questions
Is Axios justified now that fetch is everywhere?
For two or three isolated calls, no: fetch is built in, with no dependency to keep updated. On a project firing many requests at the same API, the shared instance and the interceptors save a whole file of repetitive code. Many teams also write their own wrapper around fetch, which amounts to rebuilding part of Axios.
How is an error handled?
With a try around the call. The object received carries a response property when the server answered, and its status tells a 404 apart from a 500. When that property is missing, the request never reached its destination, and the message to display is not the same one.
Can a request in flight be canceled?
Yes, by passing an AbortController signal in the call options. It is the same mechanism fetch uses, which spares learning two interfaces. The classic use case remains search-as-you-type, where every new letter must cancel the previous request.