Fetching Data — A Fundamental Aspect of Web App Networking

A detailed examination of different techniques for fetching data in frontend applications, including their strengths, pitfalls, and applications in real-world scenarios.

ShareShare

Fetching Data — A Fundamental Aspect of Web App Networking

In the realm of modern web applications, fetching data is a fundamental operation. It's the key bridging element that enables your frontend to interact with backend services and the broader web. Regardless of the type of web application, be it a data-driven dashboard, an interactive form, a dynamic feed, or a real-time chat system, there exists a critical requirement for remote data retrieval. Thus, gaining a comprehensive understanding of data fetching techniques is a cornerstone skill for frontend developers.

This discourse delves into:

  • Foundational principles of data fetching
  • Comparisons between fetch, Axios, and contemporary libraries
  • Strategies for caching, retrying, and cancellation of requests
  • React-specific strategies including lifecycle management
  • Security implications, performance considerations, and real-world challenges

The Significance of Data Fetching

In the modern web development landscape, frontend code is rarely static. It continually interacts and reacts to user interactions, API responses, and backend events. Consequently, the efficacy of your data-fetching strategy significantly impacts various aspects including:

  • Load times
  • Responsiveness
  • Perceived performance
  • Error resilience
  • Server efficiency

An ineffective data fetching strategy can lead to a poor user experience, unstable application state, excessive bandwidth utilization, and ultimately, dissatisfied users.


Fundamental Data Fetching Patterns

1. Direct Fetch

const res = await fetch("/api/data");
const json = await res.json();

At its most basic, the Fetch API provides a promise-based mechanism for making HTTP requests to the server. It is straightforward and easy to use. However, it lacks built-in support for retries, error handling, timeouts, and cancellation of requests.

2. Axios

const { data } = await axios.get("/api/data");

Axios is a promise-based HTTP client for the browser and Node.js, offering several advantages over the basic Fetch API. These include:

  • Automatic JSON parsing
  • Improved error handling with richer error object semantics
  • Support for request and response interception
  • Timeouts and cancellation of requests using cancel tokens

These features make Axios a popular choice for enterprise-level applications.

3. React useEffect Pattern

useEffect(() => {
  async function load() {
    const res = await fetch("/api/items");
    const json = await res.json();
    setData(json);
  }
  load();
}, []);

The React useEffect pattern allows you to manage side-effects, including data fetching, in your functional components. This pattern handles the component lifecycle well, but it lacks built-in mechanisms for caching, retries, and request deduplication. These shortcomings can be addressed by combining useEffect with appropriate state management solutions.


React-Specific Data Fetching Solutions

React Query / TanStack Query

const { data, isLoading, error } = useQuery("products", () =>
  fetch("/api/products").then(res => res.json())
);

React Query is a powerful library for fetching, synchronizing, and updating server state in React applications. Its key benefits include:

  • Automatic caching and deduplication of requests
  • Refetching of data when the window regains focus or when network connectivity is reestablished
  • Support for pagination and background updates
  • Integrated devtools and retry logic

SWR (from Vercel)

const { data, error } = useSWR("/api/data", fetcher);

SWR is a lightweight, hook-based library for data fetching in React applications. It uses the stale-while-revalidate HTTP caching strategy, which allows the client to use a cached response (if available) while performing a revalidation request in the background.


Key Concepts in Data Fetching

Caching

Caching refers to the practice of storing the result of a request, so subsequent requests for the same data can be served from the cache instead of making a new request to the server. This can greatly improve the performance and responsiveness of your application. You can either use data fetching libraries that provide built-in caching mechanisms or implement your own caching logic.

Deduplication

Request deduplication is a technique used to prevent multiple identical requests from being executed concurrently. This is particularly important in React when rendering lists or when working with concurrent mode.

Cancellation

It's important to be able to cancel in-flight requests in certain scenarios, such as when a component is unmounted or when the request parameters change. You can use the AbortController interface (in conjunction with the Fetch API) or Axios' cancelToken for this purpose.

const controller = new AbortController();
fetch(url, { signal: controller.signal });
controller.abort();

Pagination and Infinite Loading

When dealing with large datasets, it's often impractical and inefficient to load all the data at once. Instead, data can be loaded in chunks as needed using pagination or infinite loading techniques. This can be implemented using offset/limit parameters or cursor-based URLs in conjunction with "Load More" buttons or scroll event listeners. Libraries like React Query and SWR provide built-in hooks for this purpose.


Error Handling

Appropriate error handling is critical to ensure a good user experience. Errors should be clearly communicated to the user, and ideally, the application should provide a way to retry failed requests.

if (error) return <div>Error loading data: {error.message}</div>;

Beyond displaying error messages, you could also consider implementing a retry strategy with exponential backoff, logging errors to a service like Sentry or DataDog, and providing a fallback UI when the application is offline.


Fetching on Events

Not all data fetching operations occur on component mount. Some fetches need to be manually triggered based on specific events, such as button clicks, form submissions, tab changes, or scroll events triggered by the Intersection Observer API. In such cases, you can use the useCallback hook in conjunction with an asynchronous function.


Security Considerations

When fetching data, it's crucial to consider the security implications:

  • Always validate and sanitize data on the server-side
  • Do not blindly trust data coming from the frontend
  • Avoid exposing sensitive information in request parameters
  • Ensure communication is done over https and CORS policies are set correctly
  • If necessary, obfuscate fetch logic via proxies to conceal the backend's implementation details

Real-World Data Fetching Patterns

Many popular web applications employ sophisticated data fetching strategies:

Stripe Dashboard

Stripe's dashboard uses React Query to fetch customer and product data. It selectively polls data on specific components and cancels fetches when tabs switch or filters change.

GitHub

GitHub uses GraphQL for data fetching, with normalized caching for efficiency. It also prefetches data on hover for pull request diffs and comments and adjusts the polling rate depending on the page context.


Anti-Patterns to Avoid

While data fetching is a critical aspect of web applications, there are several common pitfalls to avoid:

  • Fetching data inside the render body of a component, which can lead to unnecessary fetches and state updates
  • Failing to handle errors or empty states, which can result in a poor user experience
  • Triggering redundant fetches on every rerender
  • Forgetting to debounce param-driven fetches, such as those triggered by search input changes
  • Ignoring the impact of slow network conditions or poor server responses

Conclusion: Fetching Data with Intent

While data fetching is relatively easy to get started with, it's a complex topic that requires a deep understanding to master. When fetching data, you're not merely requesting data. You're managing a range of considerations including network performance, user feedback, state synchronization, server load, and component lifecycle.

Therefore, always fetch data with intent, abstract smartly, and design your application as if your bandwidth was a scarce resource. Because, for many of your users, it might be.

Subscribe for Deep Dives

Get exclusive in-depth technical articles and insights directly to your inbox.

about

Ehsan Hosseini

Ehsan Hosseini

me [at] ehosseini [dot] info

Staff Software Engineer and Tech Lead with a track record of leading high-performing engineering teams and delivering scalable, end-to-end technology solutions. With experience across web, mobile, and backend systems, I help companies make smart architectural decisions, scale efficiently, and align technical strategy with business goals.

© 2025 Ehsan Hosseini. All rights reserved.