REACT Lesson 20 – API Calls with Axios | Dataplexa
LESSON 20

API Calls with Axios

Master HTTP requests with React's most popular API client - handle errors, loading states, and interceptors professionally.

Axios is like having a professional courier service for your API calls. While fetch is the basic mail service, Axios comes with package tracking, automatic retries, and premium features. Netflix uses it. So does Airbnb. You'll see why. The DataFlow team wants to upgrade from fetch to Axios. They need better error handling, request cancellation, and automatic JSON parsing. Time to make API calls feel effortless.

Why Axios Over Fetch

Fetch requires manual work. Check response.ok, parse JSON, handle errors yourself. Axios does this automatically. Compare these approaches:
// Fetch - manual everything
const fetchUsers = async () => {
  try {
    const response = await fetch('/api/users');
    if (!response.ok) {
      throw new Error('Network error');
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
  }
};
Fetch Comparison
Now the Axios version. Clean. Automatic. Professional:
// Axios - automatic everything
import axios from 'axios';

const fetchUsers = async () => {
  try {
    const response = await axios.get('/api/users');
    return response.data; // Already parsed JSON
  } catch (error) {
    console.error('Request failed:', error.response?.data);
  }
};
Axios Advantage

What just happened?

Axios automatically parsed JSON and gave us cleaner error objects. The response.data contains our actual data - no manual parsing needed. Try this: Install axios and watch your API calls become half the code.

Setting Up Axios

Install Axios first. Then create a configured instance. Think of it as your custom API client:
// Install: npm install axios
// api.js - Central configuration
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json',
  }
});

export default api;
API Configuration
Now use this configured instance across your DataFlow dashboard. Every component gets the same settings:
// UsersList.js - Using configured Axios
import React, { useState, useEffect } from 'react';
import api from './api';

function UsersList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await api.get('/users');
        setUsers(response.data);
      } catch (error) {
        console.error('Failed to fetch users:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []);

  if (loading) return <div>Loading users...</div>;

  return (
    <div>
      <h2>DataFlow Users</h2>
      {users.slice(0, 3).map(user => (
        <div key={user.id} style={{padding: '12px', border: '1px solid #e2e8f0', margin: '8px 0'}}>
          <strong>{user.name}</strong> - {user.email}
        </div>
      ))}
    </div>
  );
}
DataFlow Users

What just happened?

The configured Axios instance handled the API call automatically. Notice how response.data gave us the parsed JSON directly. Try this: Add error states and watch Axios catch network failures gracefully.

HTTP Methods with Axios

Axios makes every HTTP method feel natural. GET, POST, PUT, DELETE - all follow the same pattern. The DataFlow team needs CRUD operations for their analytics data:
// All HTTP methods with Axios
import api from './api';

// GET - Read data
const getUsers = async () => {
  const response = await api.get('/users');
  return response.data;
};

// POST - Create new
const createUser = async (userData) => {
  const response = await api.post('/users', userData);
  return response.data;
};

// PUT - Update existing  
const updateUser = async (id, userData) => {
  const response = await api.put(`/users/${id}`, userData);
  return response.data;
};

// DELETE - Remove
const deleteUser = async (id) => {
  await api.delete(`/users/${id}`);
};
HTTP Methods
Real example. DataFlow needs a component that can create new analytics reports:
// ReportForm.js - POST request with form data
import React, { useState } from 'react';
import api from './api';

function ReportForm() {
  const [report, setReport] = useState({ title: '', metrics: '' });
  const [loading, setLoading] = useState(false);
  const [success, setSuccess] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    
    try {
      await api.post('/reports', {
        title: report.title,
        metrics: report.metrics,
        createdAt: new Date().toISOString()
      });
      setSuccess(true);
      setReport({ title: '', metrics: '' });
    } catch (error) {
      console.error('Failed to create report:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        value={report.title}
        onChange={e => setReport({...report, title: e.target.value})}
        placeholder="Report title"
        required
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Create Report'}
      </button>
      {success && <div>Report created successfully!</div>}
    </form>
  );
}
DataFlow Report Creator

Error Handling and Loading States

Axios provides rich error information. Status codes, response data, request details. Use this for better user experience:
// Advanced error handling with Axios
import React, { useState, useEffect } from 'react';
import api from './api';

function DataTable() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        setError(null);
        const response = await api.get('/analytics');
        setData(response.data);
      } catch (err) {
        if (err.response) {
          // Server responded with error status
          setError(`Server error: ${err.response.status}`);
        } else if (err.request) {
          // Network error
          setError('Network error - check connection');
        } else {
          setError('Request failed');
        }
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <div>Loading analytics...</div>;
  if (error) return <div style={{color: 'red'}}>{error}</div>;
  
  return <div>Data loaded: {data.length} items</div>;
}
Error Handling Demo

What just happened?

Axios errors have three types: response (server error), request (network issue), or setup (config problem). Each gets different handling. Try this: Click the error buttons to see how each scenario displays different messages to users.

Interceptors and Global Configuration

Interceptors are middleware for your API calls. Add authentication tokens, log all requests, transform responses globally. Set them once, benefit everywhere:
// api.js - Interceptors setup
import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.dataflow.com',
  timeout: 10000
});

// Request interceptor - add auth token
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('authToken');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    console.log('Making request:', config.method, config.url);
    return config;
  },
  (error) => Promise.reject(error)
);

// Response interceptor - handle common errors
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('authToken');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default api;
Interceptor Flow
Now every API call in DataFlow automatically includes the auth token and handles 401 errors. No code changes needed in components. That's the power of interceptors.

Common Axios Gotcha

Interceptors run for ALL requests made by that Axios instance. Don't add user-specific logic that should only run in certain components. Keep interceptors global and generic.

Request Cancellation and Cleanup

Users navigate away. Components unmount. Cancel ongoing requests to avoid memory leaks and stale updates. Axios makes this clean with AbortController:
// Canceling requests in React components
import React, { useState, useEffect } from 'react';
import api from './api';

function SearchResults({ query }) {
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!query) return;

    const controller = new AbortController();
    setLoading(true);

    const searchData = async () => {
      try {
        const response = await api.get('/search', {
          params: { q: query },
          signal: controller.signal
        });
        setResults(response.data);
      } catch (error) {
        if (error.name !== 'AbortError') {
          console.error('Search failed:', error);
        }
      } finally {
        setLoading(false);
      }
    };

    searchData();

    // Cleanup: cancel request if component unmounts
    return () => controller.abort();
  }, [query]);

  return (
    <div>
      {loading && <div>Searching...</div>}
      {results.map(item => (
        <div key={item.id}>{item.title}</div>
      ))}
    </div>
  );
}
Search with Cancellation

What just happened?

The cleanup function cancels the previous request when the query changes. This prevents race conditions where a slow request returns after a fast one, showing stale results. Try this: Type quickly and watch how only the latest search completes.

Axios transforms API calls from manual labor into automated workflows. Your DataFlow dashboard now handles requests professionally - with proper error states, loading indicators, and automatic cleanup. Netflix-level API handling in your React app. The next lesson covers lifting state up. You'll learn how to share data between components without prop drilling, setting up DataFlow's component communication patterns.

Quiz

1. The DataFlow team is comparing API approaches. What's the main advantage of axios.get('/users') over fetch('/users')?


2. DataFlow needs to add auth tokens to all API calls. How do Axios interceptors work?