Data Caching
The cache function allows you to cache the results of data fetching or computation, Compared to full-page rendering cache, it provides more fine-grained control over data granularity and is applicable to various scenarios such as Client-Side Rendering (CSR), Server-Side Rendering (SSR), and API services (BFF).
Basic Usage
If you use the cache function in BFF, you should import the cache funtion from @modern-js/server-runtime/cache
import { cache } from '@modern-js/server-runtime/cache'
Parameters
fn: The data fetching or computation function to be cachedoptions(optional): Cache configurationtag: Tag to identify the cache, which can be used to invalidate the cachemaxAge: Cache validity period (milliseconds)revalidate: Time window for revalidating the cache (milliseconds), similar to HTTP Cache-Control's stale-while-revalidate functionalitygetKey: Simplified cache key generation function based on function parameterscustomKey: Custom cache key generation function, used to maintain cache when function references change
The type of the options parameter is as follows:
Return Value
The cache function returns a new function with caching capabilities. Multiple calls to this function will not re-execute the fn function.
Usage Scope
Unlike React's cache function which can only be used in server components,
Modern.js's cache function can be used in any frontend or server-side code.
Detailed Usage
Without options Parameter
When no options parameter is provided, it's primarily useful in SSR projects, the cache lifecycle is limited to a single SSR rendering request. For example, when the same cachedFn is called in multiple data loaders, the cachedFn function won't be executed repeatedly. This allows data sharing between different data loaders while avoiding duplicate requests. Modern.js will re-execute the fn function with each server request.
Without the options parameter, it can be considered a replacement for React's cache function and can be used in any server-side code (such as in data loaders of SSR projects), not limited to server components.
With options Parameter
maxAge Parameter
After each computation, the framework records the time when the cache is written.
When the function is called again, it checks if the cache has expired based on the maxAge parameter.
If expired, the fn function is re-executed; otherwise, the cached data is returned.
revalidate Parameter
The revalidate parameter sets a time window for revalidating the cache after it expires. It can be used together with the maxAge parameter, similar to HTTP Cache-Control's stale-while-revalidate mode.
In the following example, the cache behavior is divided into three phases based on time:
- 0 to 2 minutes (within
maxAge): Cache is fresh, returns cached data directly without re-fetching - 2 to 3 minutes (between
maxAgeandmaxAge + revalidate): Cache has expired but is within the revalidation window, returns stale data immediately while re-fetching and updating the cache in the background - Beyond 3 minutes (exceeding
maxAge + revalidate): Cache is completely expired, re-executes the function to fetch new data
tag Parameter
The tag parameter identifies the cache with a tag, which can be a string or an array of strings.
You can invalidate caches based on this tag, and multiple cache functions can use the same tag.
getKey Parameter
The getKey parameter simplifies cache key generation, especially useful when you only need to rely on part of the function parameters to differentiate caches. It's a function that receives the same parameters as the original function and returns a string.
Its return value becomes part of the final cache key, but the key is still combined with a unique function identifier, making the cache function-scoped.
You can also use Modern.js's generateKey function together with getKey to generate the cache key:
The generateKey function in Modern.js ensures that a consistent and unique key is generated even if object property orders change, guaranteeing stable caching.
Additionally, getKey can also return a numeric type as a cache key:
customKey parameter
The customKey parameter is used to fully customize the cache key. It is a function that receives an object with the following properties and returns a string as the cache key.
Its return value directly becomes the final cache key, overriding the default combination of a function identifier and parameter-based key. This allows you to create globally unique keys and share cache across different functions.
params: Array of arguments passed to the cached functionfn: Reference to the original function being cachedgeneratedKey: Cache key automatically generated by the framework based on input parameters
Generally, the cache will be invalidated in the following scenarios:
- The function's input parameters change
- The maxAge condition is no longer satisfied
- The
revalidateTagmethod has been called
By default, the framework generates a stable function ID based on the function's string representation and combines it with the generated parameter key. customKey can be used when you need complete control over the cache key generation, especially useful for sharing cache across different function instances. If you just need to customize how parameters are converted to cache keys, it is recommended to use getKey.
This is very useful in some scenarios, such as when you want to share cache across different function instances or when you need predictable cache keys for external cache management.
onCache Parameter
The onCache parameter allows you to track cache statistics such as hit rate. It's a callback function that receives information about each cache operation, including the status, key, parameters, and result.
The onCache callback receives an object with the following properties:
status: The cache operation status, which can be:hit: Cache hit, returning cached contentmiss: Cache miss, executing the function and caching the resultstale: Cache hit but data is stale, returning cached content while revalidating in the background
key: The cache key, which is either the result ofcustomKeyor the default generated keyparams: The parameters passed to the cached functionresult: The result data (either from cache or newly computed)
This callback is only invoked when the options parameter is provided. When using the cache function without options, the onCache callback is not called.
The onCache callback is useful for:
- Monitoring cache performance
- Calculating hit rates
- Logging cache operations
- Implementing custom metrics
Storage
Default Storage
Currently, both client and server caches are stored in memory. The default storage limit for all cached functions is 1GB. When this limit is reached, the oldest cache is removed using an LRU algorithm.
Considering that the results of cache function caching are not large, they are currently stored in memory by default.
You can specify the storage limit using the configureCache function:
Custom Storage Container
In addition to the default memory storage, you can use custom storage containers such as Redis, file systems, databases, etc. This enables cache sharing across processes and servers.
Container Interface
Custom storage containers need to implement the Container interface:
Basic Usage
Using customKey to Ensure Cache Key Stability
When using custom storage containers (such as Redis), it's recommended to configure customKey to ensure cache key stability. This ensures:
- Cross-process sharing: Different server instances can share the same cache
- Cache validity after application restart: Cache remains valid after restarting the application
- Cache persistence after code deployment: Cache for the same logic remains effective after code updates
The default cache key generation mechanism is based on function references, which may not be stable enough in distributed environments. It's recommended to use customKey to provide stable cache keys:
Redis Storage Example
Here's an example of using Redis as a storage backend:
Notes
-
Serialization: All cached data will be serialized to strings for storage. The container only needs to handle string get/set operations.
-
TTL Support: If your storage backend supports TTL (Time To Live), you can use the
options.ttlparameter in thesetmethod (in seconds).