Skip to content

DocsCore conceptsData & Caching

On this page

Data & Caching

Ramonyx keeps data loading in the page module: a loader for reads, an action for writes, and an opt-in cache for expensive work.

Loaders

ts
export const revalidate = 60;
export const cacheTags = ["catalog"];

export async function loader({ params }) {
  return { product: await db.products.find(params.id) };
}

Loader data is JSON-serializable and available to the component as data (and to the client via the hydration payload).

Cache semantics

Setting Effect
revalidate: 60 Cache loader data for 60 s; the next 60 s serves stale data and refreshes in the background
cacheTags: ["catalog"] Tag the entry so revalidate({ tags: ["catalog"] }) removes it
no revalidate No caching — loader runs on every request

Session safety: requests with cookies or a query string bypass the cache. That keeps per-user responses out of a shared cache by construction.

The default cache is in-memory (single process). The store interface (KeyValueStore) is pluggable — implement get/set/delete/invalidateTags for Redis or another backend and pass it to createCache({ store }). The contract has a shared test suite in the repo.

On-demand invalidation

ts
// from an action
await revalidate({ tags: ["catalog"] });
ts
// from anywhere with the cache instance
import { createCache } from "ramonyx";
const cache = createCache();
await cache.revalidate({ paths: ["/products"], tags: ["catalog"] });

Invalidation also fires the onInvalidate app hook, so observability and plugins can react.

Server actions

Actions are POST handlers colocated with the page:

ts
export const actionSchema = {
  "~standard": {
    version: 1,
    vendor: "app",
    validate: (form) => {
      const name = String((form as Record<string, unknown>)?.name ?? "").trim();
      return name.length > 0
        ? { value: { name } }
        : { issues: [{ message: "Name is required", path: ["name"] }] };
    },
  },
};

export async function action({ formData, revalidate }) {
  const name = String(formData.get("name"));
  await db.customers.create({ name });
  await revalidate({ tags: ["customers"] });
  return { message: `Created ${name}` };
}

actionSchema validates the form data before the action runs (invalid → 400 + field errors). The page renders the last action result as action ({ ok, errors?, message?, data? }). Redirects use 303 so a refresh does not resubmit.

Pragmatic notes

  • The cache key includes the page path, route params, and (with i18n) the active locale.
  • Cached values are process-local; for multi-instance deployments use an external store.
  • There is no HTML page cache (CSP nonces make HTML per-request); only loader data is cached.
  • A CDN can cache the HTML of static/prerendered pages separately.
Edit this page on GitHub ↗