Skip to main content

API Caching

What It Is

API caching means storing API responses in memory so the app does not need to fetch the same data from the server repeatedly.

It improves performance by reducing latency, avoiding duplicate requests, and decreasing server load.

API Caching = Store fetched API data and reuse it when needed

Why It Matters

Frontend apps often request the same data multiple times.

Without caching, every component or page may trigger a new network request for the same information.

API caching helps by:

  • reducing repeated API calls
  • making UI updates faster
  • decreasing backend load
  • improving perceived performance
  • sharing fetched data across components
First request -> fetch from server
Next request -> reuse cached response

Common API Caching Libraries

Several frontend libraries provide API caching features.

LibraryMain Use
React Query / TanStack QueryServer state caching for React apps
SWRLightweight data fetching and caching
Apollo ClientGraphQL query caching with normalized in-memory cache

These libraries handle caching, loading states, error states, background refresh, and cache updates.


React Query

React Query, also known as TanStack Query, provides hooks for fetching and caching API data.

It uses a QueryClient to manage cached server state.

QueryClient = central cache manager for React Query

React Query Setup

Create a query client and provide it to the app.

import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";

const queryClient = new QueryClient();

export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
);
}

The QueryClientProvider makes the cache available to components inside the app.


React Query Example

Use useQuery() to fetch and cache data.

function Example() {
const { isPending, error, data } = useQuery({
queryKey: ["repoData"],
queryFn: () =>
fetch("https://api.github.com/repos/TanStack/query").then((res) =>
res.json(),
),
});

if (isPending) return "Loading...";

if (error) {
return "An error has occurred: " + error.message;
}

return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>{" "}
<strong>{data.stargazers_count}</strong>{" "}
<strong>🍴 {data.forks_count}</strong>
</div>
);
}

What Happens in React Query

The query is identified using a unique key.

queryKey: ["repoData"];

The data is fetched using a query function.

queryFn: () => fetch(url).then((res) => res.json());

React Query then handles:

  • loading state
  • error state
  • response data
  • caching
  • reuse of previously fetched data
Same queryKey -> same cached data

SWR

SWR uses a global cache by default.

This allows data to be stored and shared across components.

SWR = Data fetching library with shared cache

SWR also allows custom cache behavior using the provider option inside SWRConfig.


SWR Cache Provider

A cache provider is a Map-like object.

It supports methods like:

MethodPurpose
getRead cached data
setStore cached data
deleteRemove cached data
keysGet all cache keys

TypeScript-style shape:

interface Cache<Data> {
get(key: string): Data | undefined;
set(key: string, value: Data): void;
delete(key: string): void;
keys(): IterableIterator<string>;
}

SWR Provider Example

The provider option receives a function that returns a cache provider.

import useSWR, { SWRConfig } from "swr";

function App() {
return (
<SWRConfig value={{ provider: () => new Map() }}>
<Page />
</SWRConfig>
);
}

All SWR hooks inside this SWRConfig boundary use the provided cache.

SWRConfig boundary -> shared custom cache provider

Apollo Client

Apollo Client stores GraphQL query results in a local, normalized, in-memory cache.

This allows Apollo to return already cached GraphQL data without sending another network request.

Apollo Cache = Normalized in-memory cache for GraphQL data

Apollo Cache Flow

The first time an app runs a query for data that is not in cache:

Apollo Client -> InMemoryCache
Cache miss -> GraphQL Server
Server returns data
Data stored in cache
Apollo returns data to component

Later, if the same data is requested again:

Apollo Client -> InMemoryCache
Cache hit -> returns cached data
GraphQL Server is not queried

This makes repeated queries much faster.


Apollo Cache Customization

Apollo Client cache is highly configurable.

It can be customized for:

  • individual types
  • fields in the schema
  • local data
  • GraphQL query behavior

Apollo can also store and interact with local data that is not fetched from the GraphQL server.

Apollo cache can manage both server data and local data.

Apollo Fetch Policy

By default, Apollo's useQuery() checks the cache first.

If all requested data is available locally, Apollo returns cached data and does not query the GraphQL server.

This default behavior is called:

cache-first

Fetch Policy Example

You can change the fetch policy using the fetchPolicy option.

const { loading, error, data } = useQuery(GET_DOGS, {
fetchPolicy: "network-only",
});

Here, network-only means Apollo does not check cache before making the network request.


Network Policies

Different fetch policies decide how cache and network should be used.

PolicyBehavior
cache-firstUse cache if available, otherwise fetch from network
network-onlyAlways fetch from network
cache-and-networkUse cache first and also fetch latest data from network
cache-lastFetch from network first, use cache only if network fails
no-cacheDo not read from or write to cache

cache-first

cache-first checks the cache before making a network request.

Cache available -> use cache
Cache missing -> fetch from network

This is Apollo Client's default fetch policy.

It is useful when cached data is acceptable and fast UI response is important.


network-only

network-only ignores the cache for reading and always sends a network request.

Always fetch from server

Use it when fresh data is more important than cached speed.

In Apollo, network-only may still write the result to cache after fetching.


cache-and-network

cache-and-network returns cached data first if available, while also making a network request.

When fresh data arrives, the cache updates and the component re-renders.

Show cached data quickly
Fetch latest data in background
Update UI when new data arrives

This is useful when you want both fast UI and fresh data.


cache-last

cache-last fetches from the network first.

If the network request fails, then the cache is used as a fallback.

Network first
Cache only if network fails

This is useful when fresh data is preferred but cached data is acceptable as a backup.


no-cache

no-cache bypasses the cache completely.

It does not read from cache and does not write to cache.

No cache read
No cache write
Every request is independent

Use this when every fetch must be separate and cache should not be involved.


API Caching Table

AreaExplanation
Main goalStore API responses to avoid repeated requests
Storage typeUsually in-memory cache
BenefitLower latency and server load
React QueryUses query keys and query functions
SWRUses global or custom cache providers
Apollo ClientUses normalized GraphQL in-memory cache
Default Apollo policycache-first
Freshness controlManaged through fetch policies and cache behavior

Basic Checklist

Cache API responses that are requested repeatedly
Use unique query keys for cached data
Use React Query for server state caching in React apps
Use SWR for lightweight cached data fetching
Use Apollo Client for GraphQL caching
Understand cache-first behavior
Use network-only when fresh server data is required
Use cache-and-network for fast UI plus background refresh
Use no-cache when cache should not be used at all
Avoid stale data by choosing the right fetch policy

Interview Style Answer

API caching stores responses from API calls in memory so the application does not need to fetch the same data from the server repeatedly. This improves performance by reducing latency and server load. Libraries like React Query, SWR, and Apollo Client provide caching capabilities. React Query uses a QueryClient and useQuery() with a unique queryKey and queryFn to fetch and cache data. SWR uses a global cache by default and can be customized with a Map-like cache provider through SWRConfig. Apollo Client stores GraphQL query results in a local normalized in-memory cache and uses fetch policies like cache-first, network-only, cache-and-network, cache-last, and no-cache to control whether data comes from cache, network, or both.


One-Line Summary

API Caching = Store API responses in memory so repeated data requests can be served faster with fewer server calls.

Final Mental Model

React Query -> queryKey based API cache
SWR -> shared cache provider
Apollo -> normalized GraphQL cache

cache-first -> fast cached response
network-only -> always fresh
cache-and-network -> fast now, fresh later
no-cache -> no cache involvement