> [Agent navigation index](/llms.txt)

# Data cache policies

Configure native data caching, identify eligible reads, choose sharing boundaries, and define freshness or fallback behavior.

## Configure a data cache

For a browser or one-process service, create a stable bounded memory cache outside rendering:

```ts
import { createDataCache } from "brix/cache";
import { createApiClient } from "brix/contracts";

export const dataCache = createDataCache();
export const catalogClient = createApiClient({
  baseUrl: "https://catalog.example.com",
  cache: dataCache.public("30s"),
});

```

Each `createDataCache()` call gets an isolated process-local LRU store, limited to 1,000 entries by default. Configure server ownership once when other server integrations need the same cache:

```ts
import { createMemoryDataCacheStore } from "brix/cache";
import { defineBrixServer } from "brix/server";
import { brix } from "./brix.ts";

export const brixServer = defineBrixServer(brix, {
  dataCache: {
    namespace: "storefront-production",
    store: createMemoryDataCacheStore(),
  },
});
export const { dataCache } = brixServer;

```

Use a stable application-and-environment namespace. Replicas share data only when they use that namespace and a shared store.

## Cache eligible reads

```ts
const product = await catalogClient
  .execute(getProductApiContract, { params: { productId: "123" } })
  .unwrap();

```

Only validated JSON `GET` responses participate. Mutations, text and streams, `validateResponse: false`, and `cache: false` bypass reuse. HTTP failures, malformed JSON, and schema-invalid values are never stored.

Cache-first calls share fresh results and pending loads. Identity includes the complete URL, normalized explicit headers, effective Fetch credentials, redirect policy, scope, policy, version, and tags. Omitted credentials and explicit `same-origin` share identity; `include` and `omit` remain distinct. Brix stores JSON wire data, then validates it for each caller before returning parsed output.

Visible `Cache-Control: no-store`, `no-cache`, or `private`, `Set-Cookie`, and `Vary: *` veto storage. Browsers may hide `Set-Cookie`, so sensitive browser endpoints must send JavaScript-visible `Cache-Control: private` or `no-store`.

## Choose public or private scope

```ts
const shared = dataCache.public("5m");
const account = dataCache.private(session.cacheScope, "1m");

```

Public scope explicitly permits sharing between callers. Private scope requires a nonsecret user or tenant partition. Partitioning is not authorization: authorize normally, and do not cache sessions, permissions, carts, booking drafts, or revocation-sensitive decisions.

Rotate the scope when identity or permissions change. On logout, reset or unmount user UI, then await `dataCache.invalidateAll()` before reuse. Browser-managed cookies are invisible to Brix's identity calculation; never rely on a cookie alone to partition private data.

## Freshness and fallback

Duration shorthand is cache-first. Stale fallback is explicit:

```ts
dataCache.public("10m");
dataCache.public({ ttl: "1h", staleIfError: "1d" });

```

`staleIfError` may serve expired data after retries only for network or timeout failures and HTTP 429, 500, 502, 503, or 504. It never hides authorization, not-found, cancellation, decoding, or validation failures.

Use network-first fallback when every call must try the origin:

```ts
dataCache.public({ fallbackFor: "1h" });

```

This mode does not deduplicate origin calls. It serves the last successful cacheable response only after an eligible failure. `fallbackFor` cannot be mixed with `ttl` or `staleIfError`; total retention is capped at 30 days.

Store reads happen before origin work. Read/write errors fail open to the origin, and a successful origin response may still be written after a failed read. Explicit invalidation fails loud. Store operations default to a one-second deadline. Request timeout and cancellation remain caller-specific.
