Axios Guide — What It Is, How It Works, and How to Build a Wrapper
Overview — What Is Axios?
Axios is a popular JavaScript HTTP client used to send API requests from the browser or Node.js.
It provides a friendlier API than the native fetch(), with additional features:
- Automatic JSON transformation
- Request & response interceptors
- Automatic timeout handling
- Query params handling
- Request cancellation
- Global config support
- Error handling with rich error objects
Why Use Axios Instead of fetch()?
| Feature | Fetch | Axios |
|---|---|---|
| Auto JSON parsing | ❌ | ✅ |
| Auto error handling | ❌ must manually check status | ✅ throws on non-2xx |
| Interceptors | ❌ | ✅ |
| Timeout handling | ❌ needs AbortController | ✅ built-in |
| Upload progress tracking | ❌ | ✅ |
| Simpler params handling | ❌ | ✅ |
| Easy global defaults | ❌ | ✅ |
| Request cancellation | ⚠️ possible but complex | ✅ easy |
In short: Axios → cleaner code, simpler error handling, easier token logic, better DX.
Basic Axios Usage
GET request
import axios from "axios";
const res = await axios.get("/api/users");
console.log(res.data);POST request
const res = await axios.post("/api/login", {
email: "alice@mail.com",
password: "123456",
});Send query params
axios.get("/api/products", {
params: { page: 2, sort: "newest" },
});Setting global defaults
axios.defaults.baseURL = "https://api.example.com";
axios.defaults.headers.common["Authorization"] = "Bearer token";Why Create an Axios Wrapper?
Using Axios directly in every component causes problems:
Repeated code everywhere
- base URL
- authorization token
- error handling
- redirect logic
- toasts
- headers
- passing cookies
- parsing FormData
- logger
- response normalization
A wrapper fixes all of these.
Benefits of an Axios wrapper
- Centralized error handling
- Auth token automatically attached
- Redirect to login automatically
- Consistent toasts
- Prevent duplicate code
- Handle FormData dynamically
- Cookie reading in one place
- Body/params abstraction
- Reusable custom instance
- Optimize URL placeholders
Creating a reusable Axios instance
const axiosInstance = axios.create({
baseURL: `${process.env.baseUrl}`,
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
});Why?
- Avoid repeating
baseURL - Give all requests default headers
- Allow interceptors later
- Makes it easier to replace backend URLs later
The Wrapper Function
export const api = async function <T = any>(url: string, options: APIOptions = { showToaster: true }): Promise<T> {Why?
- Universal API function for all components
- Generic type
<T>makes response typed - Configurable options per request
Token Attachment
// Get cookie with some package
const token = 'YOUR_COOKIE';
headers: {
Authorization: token ? `Bearer ${token}` : "",
}Why?
- Automatically attaches the
Authorizationheader - No need to manually pass token per request
- Better security and consistency
Handling FormData Automatically
"Content-Type":
options.body instanceof FormData
? "multipart/form-data"
: "application/json",Why?
- Switching content type based on body
- Useful for file uploads
- Zero manual effort
Error Handling
if (status === 401) {
// Do sometime here
}Features:
- Auto redirect to login on
401 - Auto clear invalid tokens
- Auto show toast with backend message
- Ability to disable toasts per request
Throw Error Again
throw err;Why?
You want the component to still react:
- show UI fallback
- disable button
- handle specific errors
Otherwise the wrapper would “eat” the error.
Example Usage
GET request
const user = await api("/user/profile");POST request with body
await api("/auth/login", {
method: "POST",
body: { email, password }
});POST FormData
const fd = new FormData();
fd.append("file", myFile);
await api("/upload", {
method: "POST",
body: fd
});References
Summary
-
Axios simplifies HTTP requests and improves DX compared to
fetch(). -
A wrapper gives you a centralized layer for:
- tokens
- errors
- toasts
- redirects
- dynamic URL injection
- JSON/Multipart handling
-
The wrapper you provided is clean, scalable, and production-ready.
-
It ensures consistent behavior across the entire codebase and reduces repeated logic.