Caching
@lazarv/react-server
provides a caching mechanism for the rendering response and a built-in in-memory cache for any value with TTL outage and compound cache keys.
You can enable response caching when you use a server component with the withCache
wrapper or using the useResponseCache
hook. Not only the wrapped component or the component where you used the cache hook will be cached, but the entire HTTP response where the cache was enabled.
The response cache is using both a cache provider and HTTP Cache-Control
with stale-while-revalidate
. The server-side cache is used for any subsequent requests until the cache is invalidated. The client-side cache is used for any subsequent requests from the same client.
import { withCache } from "@lazarv/react-server";
export default withCache(async function App() {
return <div>{Math.random()}</div>;
}, 30 * 1000);
import { useResponseCache } from "@lazarv/react-server";
export default async function App() {
useResponseCache(30 * 1000);
return <div>{Math.random()}</div>;
}
You can use the in-memory cache by importing the useCache
helper function from @lazarv/react-server
. You can use this caching solution to cache any async value with a TTL outage and a compound cache key. The cache is shared between all server components.
import { useCache } from "@lazarv/react-server";
import { readFile } from "node:fs/promises";
export default async function FileContent({ filename }) {
const file = await useCache(
["file", filename],
async () => readFile(filename, "utf-8"),
30 * 1000,
);
return <pre>{file}</pre>;
}
You can use the revalidate
function to revalidate the cache using a compound key. Calling this function will instantly invalidate the cache for the given key. This function is only available in server components.
import { revalidate } from "@lazarv/react-server";
export default async function App() {
return (
<div>
<FileContent filename="temp.txt" />
<form
action={async () => {
"use server";
revalidate(["file", filename]);
redirect("/");
}}
>
<button type="submit">Refresh</button>
</form>
</div>
);
}
The "use cache"
directive can be used to enable caching in any function. The directive accepts the profile
, ttl
, and tags
options. The profile
option is used to specify the cache profile to use. The ttl
option is used to specify the time-to-live for the cache in milliseconds, which overrides the ttl
option from the cache profile. The tags
option is used to specify the tags for the cache key.
You can use the tags
option to specify a comma-separated list of tags to address when invalidating the cache for a specific group of tags. For example, if you have a function that fetches todos and you want to invalidate the cache for all todos, you can use the tags
option to add the todos
tag to the cache key.
App.jsximport { invalidate } from "@lazarv/react-server";
async function getTodos() {
"use cache; ttl=200; tags=todos";
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
return {
timestamp: Date.now(),
data: await res.json(),
};
}
export default async function App() {
const todos = await getTodos();
return (
<form
action={async () => {
"use server";
invalidate(getTodos);
}}
>
<button type="submit">Refresh</button>
<pre>{JSON.stringify(todos, null, 2)}</pre>
</form>
);
}
You can use both "use cache"
and "use server"
directives in the same file as both directives are server-only, but you cannot use the "use cache"
directive in a client component.
While invalidate
is an async function to support any cache implementation in the future, the "use cache"
directive is using the in-memory cache by default right now and so you don't need to await the invalidate
function as the in-memory cache is synchronous.
Cache profiles are defined in the server configuration. You can specify any number of cache profiles and reference them by name in the "use cache"
directive. The cache profile can include the ttl
and tags
options which will be used when the "use cache"
directive does not specify them.
react-server.config.json{
"cache": {
"profiles": {
"todos": { "ttl": 30000, "tags": "todos" }
}
}
}
After defining the cache profiles, you can reference them by name in the "use cache"
directive.
App.jsxasync function getTodos() {
"use cache; profile=todos";
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
return {
timestamp: Date.now(),
data: await res.json(),
};
}
The "use cache"
directive currently will use the in-memory cache. Custom cache adapters are not yet supported for the "use cache"
directive.
You can specify a cache adapter to use instead of the default in-memory cache. The default cache adapter is @lazarv/react-server/memory-cache
. You can specify a different cache adapter by using the cache
option in the server configuration. The cache adapter should be a module that exports an init$
function that returns a cache instance. The cache should implement the ReactServerCache
interface from @lazarv/react-server
. The example implementation of a cache adapter is the @lazarv/react-server/memory-cache
module which you can find at packages/react-server/memory-cache/index.mjs.
export default {
cache: {
module: "./src/custom-cache",
},
};