In the realm of application development as a Frontend Engineer, the practice of caching requests is not a one-size-fits-all approach or a ubiquitous technique. Depending on the specific requirements and dynamics of the data being accessed, caching may or may not be a common practice. For instance, when dealing with real-time data from a constantly changing API, such as live currency prices that update every few seconds, it is often necessary to retrieve the latest information directly without relying on cached responses. However, in other scenarios where certain data remains static over time, subscribing to API gateways and making repeated requests can lead to unnecessary strain on resources and potential limitations. In such cases, it becomes advantageous to implement request caching to optimize API utilization and ensure that data retrieval remains within defined limits. By selectively employing caching strategies based on the nature and volatility of the data, applications can strike a balance between performance and resource management.
A Simple API Call
We can handle this in a try-catch block, making asynchronous calls.
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
Initially, these calls may appear harmless, but they can swiftly pose issues as an application scales. As the API gateway experiences a surge in incoming requests, the overall number of requests can multiply, potentially leading to a misleading perception of increased user activity. To address this, one effective optimization technique involves employing the useCallback
hook to regulate the frequency of function calls and prevent unnecessary rerendering. However, this approach is only suitable in specific scenarios: when data updates frequently and there is no requirement for request limitations on the API, such as in a subscription plan.
While there are other cons associated with scaling applications, this particular issue can be addressed through the use of caching. By caching responses from the API, developers can reduce the number of requests made to the API and thereby avoid the scaling issues that can arise from such calls.
Caching can be implemented in various ways, such as using a dedicated caching server or storing responses in local memory. Regardless of the method used, caching can significantly improve the performance of an application and reduce the number of requests made to external APIs.
TIPS ON CACHING
Here are some tips for handling HTTP caching in the JavaScript Frontend (React, Vue, etc.)
Use HTTP caching headers: if you're sure the server is sending header responses like
ETag
andLast-Modified
can be used to indicate whether a resource has changed since it was last accessed. Read moreUse browser caching: The browser can cache resources based on the cache-control headers sent by the server. You can use caching techniques such as browser cache, service workers, and application cache to store the resources in the browser cache. This can help to reduce network traffic and improve the performance of your application. Read more
Use conditional requests: Conditional requests are requests that include the
ETag
orLast-Modified
header. If the resource has not changed since the last request, the server can respond with a304 Not Modified
response, indicating that the client can use the cached version of the resource. Read moreImplement cache invalidation: When a resource is updated, it's important to invalidate any cached copies of that resource. One way to do this is by setting an expiration time on the cached resource. Another way is to use a cache invalidation mechanism such as cache-busting or versioning. Read more
By implementing these techniques, you can improve the performance of your REST API and reduce server load. I will showcase a couple of code examples using both fetch and axios
.
Here is an example using the fetch API:
import { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/resource', { cache: 'force-cache' })
.then(response => response.json())
.then(data => setData(data))
.catch(error => console.log(error));
}, []);
return (
<div>
{data && <p>{data.name}</p>}
</div>
);
}
In this example, we use the fetch
API to retrieve a resource from the server. We set the cache
option to 'force-cache'
to ensure that the browser cache is used to fetch the resource. The browser will check the cache-control headers sent by the server to determine whether to use the cached version of the resource or fetch a new copy. If the cached version is used, the server will respond with a 304 Not Modified
status code. If the resource has changed, the server will respond with a new copy of the resource.
Using axios:
import { useState, useEffect } from 'react';
import axios from 'axios';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
axios.get('/api/resource', { headers: { 'Cache-Control': 'no-cache' } })
.then(response => setData(response.data))
.catch(error => console.log(error));
}, []);
return (
<div>
{data && <p>{data.name}</p>}
</div>
);
}
In this example, we use the axios.get
method to retrieve a resource from the server. We set the Cache-Control
header to no-cache
in the request headers to ensure that the browser cache is bypassed and the server is always queried for the latest version of the resource.
Axios also provides a maxAge
option that can be used to set the maximum age for the cache in milliseconds. This option can be useful when the server sends a Cache-Control
header that specifies a maximum age for the cache. Here is an example:
import { useState, useEffect } from 'react';
import axios from 'axios';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
axios.get('/api/resource', { maxAge: 3600000 }) // Cache for 1 hour
.then(response => setData(response.data))
.catch(error => console.log(error));
}, []);
return (
<div>
{data && <p>{data.name}</p>}
</div>
);
}
In this example, we set the maxAge
option to 3600000 milliseconds (1 hour) to cache the resource in the browser cache for that duration. Axios will use the browser cache to fetch the resource if it is not older than the specified duration.
Conclusion
In conclusion, caching requests may not be universally required or commonly implemented in applications. There are situations where real-time data from a dynamic API, such as live currency prices that update frequently, necessitate direct fetching without caching. However, when subscribing to certain API gateways and retrieving static data that remains unchanged over time, it is advisable to employ request caching. By caching these requests, excessive API calls can be avoided, preventing potential limits from being exceeded. Implementing caching selectively based on the nature of the data and its volatility ensures efficient utilization of API resources while maintaining optimal performance.