React Fetching Data with Axios
Axios is a popular JavaScript library for making HTTP requests. It works in browsers and Node.js, handles JSON automatically, and offers features that the built-in fetch API does not — like request cancellation, interceptors, and automatic error handling for non-2xx responses. Most professional React projects use Axios for all API communication.
Installing Axios
npm install axios
Axios vs Fetch: Key Differences
Feature fetch() Axios
------------------------------------------------------------
JSON parsing Manual (.json()) Automatic
Error on 4xx/5xx NO (must check) YES (throws error)
Request cancellation AbortController CancelToken / AbortSignal
Request/response intercept NO YES (interceptors)
Progress tracking Limited Supported
Default timeout None Configurable
Bundle size 0 (native) ~13 KB
Why Axios throws errors on 4xx/5xx
// fetch — does NOT throw on 404 or 500
const res = await fetch("/api/user/999");
// res.ok is false but no error is thrown!
// You must check manually:
if (!res.ok) throw new Error("Request failed");
// Axios — throws automatically on 4xx/5xx
try {
const res = await axios.get("/api/user/999");
} catch (error) {
// Caught automatically for 404, 500, etc.
console.error(error.response.status); // 404
}
Basic GET Request
import axios from "axios";
import { useState, useEffect } from "react";
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
setUsers(response.data); // Axios puts the data in .data
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Loading users...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name} – {user.email}</li>
))}
</ul>
);
}
Using async/await with Axios
function PostDetail({ postId }) {
const [post, setPost] = useState(null);
useEffect(() => {
const fetchPost = async () => {
try {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
setPost(response.data);
} catch (error) {
console.error("Failed to fetch post:", error.message);
}
};
fetchPost();
}, [postId]);
if (!post) return <p>Loading...</p>;
return (
<article>
<h2>{post.title}</h2>
<p>{post.body}</p>
</article>
);
}
POST, PUT, and DELETE Requests
POST – Create a new resource
const createPost = async (newPost) => {
try {
const response = await axios.post(
"https://jsonplaceholder.typicode.com/posts",
newPost // Axios serializes this to JSON automatically
);
console.log("Created:", response.data);
} catch (error) {
console.error("Error:", error.message);
}
};
// Call it
createPost({ title: "New Post", body: "Content here", userId: 1 });
PUT – Update an existing resource
const updatePost = async (id, updatedData) => {
const response = await axios.put(
`https://jsonplaceholder.typicode.com/posts/${id}`,
updatedData
);
return response.data;
};
DELETE – Remove a resource
const deletePost = async (id) => {
await axios.delete(`https://jsonplaceholder.typicode.com/posts/${id}`);
console.log("Post deleted");
};
Sending Headers (Authentication)
Most APIs require an authorization token in the request headers.
const getProfile = async () => {
const token = localStorage.getItem("token");
const response = await axios.get("/api/profile", {
headers: {
Authorization: `Bearer ${token}`,
},
});
return response.data;
};
Axios Instance: Centralized Configuration
Create an Axios instance to set the base URL and default headers once. All requests using this instance inherit the configuration.
// api.js
import axios from "axios";
const api = axios.create({
baseURL: "https://api.myapp.com/v1",
timeout: 10000, // 10 seconds
headers: {
"Content-Type": "application/json",
},
});
export default api;
// In any component
import api from "./api";
const fetchDashboard = async () => {
const response = await api.get("/dashboard"); // becomes /api.myapp.com/v1/dashboard
return response.data;
};
Interceptors: Global Request and Response Handling
Interceptors run on every request or response. Use them to attach tokens automatically or handle expired sessions globally.
Diagram: Interceptor pipeline
Component calls api.get(url)
↓
Request Interceptor
└── Attach Authorization header
↓
HTTP Request sent to server
↓
Response Interceptor
└── If 401 → redirect to /login
└── If OK → pass data through
↓
Component gets response.data
// api.js
import axios from "axios";
const api = axios.create({ baseURL: "https://api.myapp.com" });
// Request interceptor — attach token to every request
api.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor — handle 401 globally
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
window.location.href = "/login";
}
return Promise.reject(error);
}
);
export default api;
Request Cancellation
Cancel a request when a component unmounts to prevent updating state on an unmounted component.
useEffect(() => {
const controller = new AbortController();
axios
.get("/api/data", { signal: controller.signal })
.then((res) => setData(res.data))
.catch((err) => {
if (axios.isCancel(err)) return; // Ignore cancellation errors
setError(err.message);
});
return () => controller.abort(); // Cancel on unmount
}, []);
Building a Reusable Data Fetching Hook with Axios
import { useState, useEffect } from "react";
import axios from "axios";
function useAxios(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const fetch = async () => {
try {
const res = await axios.get(url, { signal: controller.signal });
setData(res.data);
} catch (err) {
if (!axios.isCancel(err)) setError(err.message);
} finally {
setLoading(false);
}
};
fetch();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// Usage
function Posts() {
const { data: posts, loading, error } = useAxios("/api/posts");
if (loading) return <p>Loading...</p>;
if (error) return <p>{error}</p>;
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
Summary
Axios simplifies HTTP requests with automatic JSON parsing, error throwing on bad status codes, and a powerful interceptor system. Install it with npm install axios and import it in any component or utility file. Create an Axios instance to centralize base URL and header configuration across all requests. Use interceptors to attach authentication tokens globally and handle session expiry in one place. Cancel requests with AbortController when components unmount. Build a reusable useAxios custom hook to avoid duplicating fetch logic across components.
