Skip to main content
@spa-tools/api-client

Caching

Like throttling, caching is crucial for high-scale backend APIs, but when should you consider caching API results on the frontend?

Simply, if the same slice of API data is utilized in multiple places within your app, then caching should be an option to consider. True, there are many differrent approaches to tackle this scenario...from centralized state management like Redux or MobX, to other approaches like React Context, reducer hooks, and even passing data to children via props. While each of these tactics may have merit, one must admit that they also come with drawbacks and annoyances.

Wouldn't it be nice to just call your data client from anywhere in your app without having to worry about annoying boilerplate and configuration bloat? Good news is the @spa-tools/api-client has you covered with built-in caching that is super-easy!

import { callEndpoint } from '@spa-tools/api-client';

async function getAlbumPhotos(albumId: number) {
const result = await callEndpoint(
'https://jsonplaceholder.typicode.com/albums/:albumId/photos',
// pass in an options object to configure the cache settings
{
consoleOptions: {
// turn console logging ON for cache hits so we can
// easily see every time a request is cached, which
// is useful for debugging and understanding how the
// cache is working; however, it's advised to keep
// this turned off in production environments
logThrottleCacheHits: true,
//
// set the threshold for the number of cache hits that can
// occur within the configured TTL before an error-warning
// is logged to the console
//
// the real-world goal here is to use this error-warning as
// a bug indicator to know when your app is making too many
// dupe requests. The threshold is of course dependent on
// how data calls are made in conjunction with your render
// logic. For example, a React app that makes data requests
// via useEffect hooks can easily result in unexpected dupe
// calls that are par for the course, so a higher threshold
// setting would make sense in that scenario.
logThrottleWarningsThreshold: 3,
},
frequencyOptions: {
// set the frequency strategy to 'memory-cache'; however,
// you can also set this to 'session-cache' if you would
// prefer to not cache data in memory, but rather in the
// browser's session storage
frequencyStrategy: 'memory-cache',
// set the time-to-live for the cache (in ms), meaning
// a dupe call will be allowed after this time has passed
frequencyStrategyTTL: 300,
},
},
// set state object to interpolate path parameter value
{ albumId }
);

console.log(`Photos for album with ID "${albumId}":`);
console.log(result?.data);
}

// first (2) calls are distinct and will be made to API endpoint
await getAlbumPhotos(4);
await getAlbumPhotos(5);
// for the following (3) calls, results will be served from cache
// and no more calls will be made to the API endpoint
await getAlbumPhotos(5);
await getAlbumPhotos(5);
await getAlbumPhotos(5);

setTimeout(async () => {
// cache will expire after 300ms so this call will be made
// and refreshed data will be returned
await getAlbumPhotos(5);
}, 350);