On this page

Caching in Server Components

Data fetching in Rebask is handled by React Server Components, but we add additional features to cover some gaps:

  • durableCache: cache data across requests and builds.
  • "use cache": cache the result of an async function across requests.
  • Fetch deduping: identical safe GET/HEAD fetches within the same request are deduped automatically.
  • Cache Lifetimes: set revalidation windows and tags on cache entries.

By default, entries stay fresh for one hour and remain usable for one year. Use cacheLife() to change either value.

Fetching in Server Components

You can fetch data directly in async Server Components:

export default async function ProductsPage() {
  const res = await fetch("https://api.example.com/products")
  const products = await res.json()

  return <ProductList products={products} />
}

Go to the React docs to learn more about async Server Components .

Identical safe GET/HEAD fetches in one request are deduped by default. Pass signal to opt out:

const controller = new AbortController()
const res = await fetch("https://api.example.com/products", { signal: controller.signal })

A signal means the caller controls the request lifecycle, so deduping can interfere with cancellation.

durableCache

Cache a JSON-serializable value returned by an async function across requests and builds. Unlike "use cache", entries are not scoped to a deployment ID, so a cache handler that persists entries can reuse them across builds.

import { durableCache, cacheLife, cacheTag } from "@rebask/core/cache"

export const getProducts = durableCache(async (category: string) => {
  const res = await fetch(`https://api.example.com/products?cat=${category}`)
  const data = await res.json()
  cacheLife({ revalidate: 300, expire: 3600 })
  cacheTag("products", `products:${category}`)
  return data
})

Use the optional second argument to include values that affect the cache but aren't function arguments, such as environment variables:

export const getProducts = durableCache(
  async (category: string) => {
    const res = await fetch(`${process.env.API_URL}/products?cat=${category}`)
    return res.json()
  },
  [process.env.API_URL],
)

The cache key combines a hash of the function source, a SHA-256 hash of the serialized arguments, and the serialized deps. Arguments and deps must be JSON-serializable: strings, finite numbers, booleans, null, undefined, arrays, and plain objects. Non-serializable arguments, such as a promise or a Map, throw when the function is called. Non-serializable deps throw when durableCache() is created.

Persisting durableCache entries lets new builds reuse unchanged data, but also lets them serve entries from earlier builds when keys stay unchanged. Changes to environment variables or the returned data shape must therefore be reflected in the cache key or deps to avoid conflicts.

"use cache"

Caches the result of an async function.

import { cacheLife, cacheTag } from "@rebask/core/cache"

export async function getProduct(id: string) {
  "use cache"
  cacheLife({ revalidate: 300, expire: 3600 })
  cacheTag("product", `product:${id}`)
  return db.products.find(id)
}

The generated cache key includes the deployment id, module path, function name, and the encoded arguments. When the arguments encode to FormData, Rebask hashes it with SHA-256 first.

See Server Cache Keys to learn why "use cache" entries include a deployment ID.

Server component cache lifetimes and invalidation

Page, RSC, and Server Component cache entries share the same lifetime, tag, and invalidation model. See Cache Lifetimes for the concepts and APIs.

See Cache Handler to configure a custom server cache storage implementation.