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.

Leave a Comment

Your email address will not be published. Required fields are marked *