Fetching Layers — Understanding the Architecture
Overview
When building modern React applications, data fetching involves multiple layers working together. Understanding these layers helps you build scalable, maintainable applications with proper separation of concerns.
The Fetching Layers
In a typical React application, data fetching follows this layered architecture:
┌─────────────────────────────────────┐
│ React Components (UI Layer) │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ TanStack Query (State Layer) │
│ - Caching │
│ - Synchronization │
│ - Background updates │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Axios (HTTP Client Layer) │
│ - Request/Response handling │
│ - Interceptors │
│ - Error handling │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Fetch API (Network Layer) │
│ - Native browser API │
└─────────────────────────────────────┘Layer 1: Fetch API (Network Layer)
The Fetch API is the native browser API for making HTTP requests. It’s the foundation that all other layers build upon.
Characteristics:
- Native browser API
- Low-level HTTP requests
- Manual error handling required
- No automatic JSON parsing
- No request cancellation by default
Example:
const response = await fetch('/api/users')
const data = await response.json()Layer 2: Axios (HTTP Client Layer)
Axios sits on top of the Fetch API, providing a more developer-friendly interface for making HTTP requests.
Why Axios?
- Automatic JSON parsing — No need to call
.json() - Request/Response interceptors — Centralized request modification
- Better error handling — Throws errors on non-2xx status codes
- Request cancellation — Built-in support for canceling requests
- Global configuration — Set defaults for base URL, headers, etc.
What Axios Provides:
-
Wrapper Function: A centralized API function that handles:
- Base URL configuration
- Authorization tokens
- Error handling
- Toast notifications
- Request/response transformation
- FormData handling
-
Interceptors: Modify requests/responses globally:
- Add auth tokens automatically
- Handle errors consistently
- Transform response data
Example:
import axios from 'axios'
const api = axios.create({
baseURL: 'https://api.example.com',
headers: {
'Authorization': `Bearer ${token}`
}
})
const response = await api.get('/users')
// response.data is already parsedLayer 3: TanStack Query (State Management Layer)
TanStack Query (formerly React Query) manages server state on top of your HTTP client (Axios).
Why TanStack Query?
- Automatic caching — Reduces unnecessary network requests
- Background updates — Keeps data fresh automatically
- Loading/Error states — Built-in state management
- Optimistic updates — Update UI before server confirms
- Request deduplication — Multiple components requesting same data = one request
- SSR support — Prefetch and hydrate data on the server
What TanStack Query Provides:
-
Query Management:
- Cache data automatically
- Refetch on window focus (configurable)
- Stale-while-revalidate pattern
- Request deduplication
-
Mutations:
- Handle POST/PUT/DELETE operations
- Optimistic updates
- Cache invalidation
- Error rollback
-
Advanced Features:
- Infinite queries for pagination
- Parallel queries
- Suspense integration
- Server-side prefetching
Example:
import { useQuery } from '@tanstack/react-query'
import { api } from './api' // Your Axios wrapper
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => api('/users') // Uses your Axios wrapper
})How They Work Together
Complete Flow:
- Component calls
useQueryhook - TanStack Query checks cache:
- If cached and fresh → return cached data
- If stale or missing → proceed to fetch
- TanStack Query calls your query function (which uses Axios)
- Axios wrapper handles:
- Adding auth tokens
- Setting base URL
- Error handling
- Response transformation
- Axios uses Fetch API under the hood
- Response flows back up through the layers
Example: Complete Integration
// 1. Axios wrapper (HTTP Client Layer)
const api = async function <T>(url: string, options?: APIOptions): Promise<T> {
const token = getToken()
const response = await axiosInstance.get(url, {
headers: {
Authorization: `Bearer ${token}`
},
...options
})
return response.data
}
// 2. TanStack Query hook (State Management Layer)
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: () => api<User[]>('/users') // Uses Axios wrapper
})
// 3. React Component (UI Layer)
function UsersList() {
const { data, isLoading } = useQuery({...})
if (isLoading) return <div>Loading...</div>
return <ul>{data?.map(user => <li>{user.name}</li>)}</ul>
}Benefits of This Layered Approach
Separation of Concerns
- Fetch API: Network communication
- Axios: HTTP client logic (auth, errors, interceptors)
- TanStack Query: State management (caching, synchronization)
- Components: UI rendering
Reusability
- Axios wrapper can be used anywhere (components, server actions, etc.)
- TanStack Query hooks can be shared across components
- Consistent error handling and auth across the app
Maintainability
- Changes to API structure only affect Axios layer
- Changes to caching strategy only affect TanStack Query layer
- Components remain simple and focused on UI
Performance
- TanStack Query reduces unnecessary requests through caching
- Request deduplication prevents duplicate API calls
- Background refetching keeps data fresh without blocking UI
Summary
Understanding the fetching layers helps you:
- Choose the right tool for each layer
- Build maintainable data fetching architecture
- Optimize performance through proper caching
- Handle errors consistently across your app
- Scale your application as it grows
Next Steps:
- Learn how to build an Axios wrapper for your HTTP client layer
- Discover how to use TanStack Query for server state management