4 FULLSTACK SYNERGY

API Integration & Axios Interceptors

🌉 API Integration: Connecting React to Express

For the first 3 phases of this curriculum, our Frontend and Backend lived in total isolation. React rendered fake, hardcoded arrays of data. Express created raw JSON and spat it into the void.

Fullstack Synergy is the art of connecting these two systems across the internet. We must build a bridge that safely, efficiently, and asynchronously transports data from PostgreSQL, through Node.js, over the Atlantic Ocean, and into React's DOM.


1️⃣ The Tools of the Trade: Fetch vs Axios

To make an HTTP Request from a browser, we need a client.

The Native fetch() API

Built directly into the browser. Requires absolutely zero installation. The downside: It requires manual parsing of JSON, and it infuriatingly does not throw a Catch error if the server returns a 404 Not Found or 500 Server Error.

try {
    const response = await fetch('http://localhost:5000/api/users');
    
    // We must manually throw the error on 400/500 status codes!
    if (!response.ok) throw new Error("Server rejected our request.");
    
    // We must manually parse the text chunk into a JavaScript Object
    const data = await response.json(); 
    console.log(data);
} catch (error) {
    console.log("Network failure", error);
}

The Industry Standard: axios

Axios is a third-party NPM package used by 90% of enterprises. It automatically parses JSON, automatically throws errors on 400/500 codes, and allows us to build powerful "Interceptors".

npm install axios
import axios from 'axios';

try {
    // Axios handles the JSON parsing and Error throwing under the hood!
    const response = await axios.get('http://localhost:5000/api/users');
    
    // The data is instantly ready to use inside the 'data' property
    console.log(response.data); 
} catch (error) {
    // Axios instantly catches the 404/500 and triggers this block.
    console.log("Server responded with error:", error.response.status);
}

2️⃣ Building an API Service Class

If you write axios.get('http://localhost:5000/api/users') directly inside 15 different React components, you have created a nightmare.

What happens when you deploy to Production? The URL is no longer localhost:5000, it is https://api.mywebsite.com. You would have to manually find & replace 15 different files.

Professional Architecture dedicates a completely separate Service File just for API calls.

// src/services/apiService.js
import axios from 'axios';

// 1. Create a master Axios Instance. We define the Base URL exactly once.
const API = axios.create({
    baseURL: process.env.REACT_APP_API_URL || 'http://localhost:5000/api'
});

// 2. Export isolated math functions that React can consume
export const UserAPI = {
    // GET request
    fetchAll: async () => {
        const res = await API.get('/users');
        return res.data;
    },
    // POST request passing a payload body
    create: async (userData) => {
        const res = await API.post('/users', userData);
        return res.data;
    }
};

3️⃣ Consuming the API inside React (useEffect)

Now that the Service is built, we import it inside a Component's Effect hook.

import { useState, useEffect } from 'react';
import { UserAPI } from '../services/apiService';

function UserList() {
    const [users, setUsers] = useState([]);
    const [isLoading, setIsLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const loadSystem = async () => {
            try {
                // Instantly call our clean API layer!
                const data = await UserAPI.fetchAll();
                setUsers(data);
            } catch (err) {
                setError("Failed to boot up systems. Check your Wi-Fi.");
            } finally {
                // Execution hits here regardless of success or failure
                setIsLoading(false); 
            }
        };

        loadSystem();
    }, []); // Empty array! We only fetch once on component mount.

    // Conditional Rendering Steps
    if (isLoading) return <div className="spinner">Loading Data...</div>;
    if (error) return <div className="text-red-500 font-bold">{error}</div>;

    return (
        <ul>
            {users.map(u => <li key={u.id}>{u.name}</li>)}
        </ul>
    );
}

4️⃣ The Next Level: React Query (TanStack)

Manually wiring isLoading, isError, and data state variables in every single component is tedious.

Furthermore, useEffect lacks caching. If a user clicks "Home", then "About", then clicks "Home" again, standard React forces the browser to re-download the exact same data from the server.

The enterprise solution to this is React Query. It handles all loading states automatically, caches data in memory, and prevents redundant network requests.

npm install @tanstack/react-query
import { useQuery } from '@tanstack/react-query';
import { UserAPI } from '../services/apiService';

function AdvancedUserList() {
    // Look at how little code this takes!
    // React Query manages the state perfectly behind the scenes.
    const { data: users, isLoading, isError } = useQuery({
        queryKey: ['systemUsers'], // The unique ID for the Ram Cache
        queryFn: UserAPI.fetchAll  // The function to execute if the Cache is empty
    });

    if (isLoading) return <p>Loading...</p>;
    if (isError) return <p>Crash!</p>;

    return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

💡 Summary Hierarchy

ToolPurposeReal-World Status
fetch()Native browser network tool.Good for quick scripts, tedious for large apps.
Axios3rd party networking library.The industry standard for sending HTTP.
API ClassIsolating URL strings out of UI.Mandatory architecture rule.
React QueryHandles loading, error, and caching state.The modern enterprise standard replacing custom useEffects.

Knowledge Check

Complete this quick quiz to verify your understanding and unlock the next module.