frameworkEdit this page

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.jsx
import { 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.jsx
async 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",
  },
};