DOCUMENTATION Docs Powered by Fuzor Docs

Server data

Server data fills parts of a prerendered page with values computed per request. For endpoints that your own code calls, such as form posts or JSON APIs, use API endpoints instead; both make the build server-backed.

Pages are compiled ahead of time. When a page needs values that depend on the request, such as the signed-in user or live stock levels, add a server.ts next to its index file. The document stays precompiled; the browser requests the data separately and fills it in.

pages/
└── account/
    ├── index.html
    └── server.ts
// pages/account/server.ts
import { defineServerData } from "fuzor/server";

export default defineServerData(async ({ request, url, params, signal }) => {
  const user = await findUser(request.headers.get("cookie"), { signal });
  return { user: { name: user.name }, tab: url.searchParams.get("tab") ?? "profile" };
});
<!-- pages/account/index.html -->
<p>Signed in as <fuzor-data name="user.name">…</fuzor-data></p>

Server output is automatic

Any server.ts under pages/ makes every build of the app server-backed, for SPA and MPA alike. fuzor build --target mpa writes dist-mpa-server/ and prints which modules required it. The generated server entry bundles the data modules; documents remain strings selected per request.

Static-only settings that cannot work are errors: --inline, fuzorPlugin({ server: false }) and FUZOR_SERVER=0.

Using the data

  • <fuzor-data name="user.name">fallback</fuzor-data> shows its fallback until the value arrives, then its text. Names are dot paths; array items use numbers (items.0.title). Strings, numbers and booleans are shown; other values keep the fallback and set data-fuzor-state="missing".
  • Slots expose data-fuzor-state: pending, ready, missing or failed. Use it for loading styles.
  • <fuzor-loading delay="200" min="500">Loading…</fuzor-loading> anywhere outside islands appears only while a request takes longer than delay, and once visible stays at least min milliseconds; values are filled in as soon as they arrive. See loading indicators.
  • Islands call routeData(host) from fuzor/runtime/data. They share the page's single request.
  • The page element dispatches fuzor:data with { data } and fuzor:data-error with { error }.

The SPA router starts the request when a page is committed, before islands activate, cancels it when you navigate away and requests fresh data when only the query string changes. The page's query string is forwarded to the loader.

Loaders

A loader receives request, url, route params (for pages in dynamic folders) and an abort signal. It can return a value, a promise or an Effect. Effects can read the same context from the ServerRequest service:

import * as Effect from "effect/Effect";
import { defineServerData, notFound, ServerRequest } from "fuzor/server";

export default defineServerData(() => Effect.gen(function* () {
  const { params } = yield* ServerRequest;
  const user = yield* findUser(params.id);
  return user ? { user } : notFound();
}), { timeoutMs: 5000 });
ResultResponse
A JSON-serializable value200 with { "data": … }
notFound()404
A thrown error, failed Effect or unserializable value500; details go to onError, never to the browser
Exceeding timeoutMs504

Caching and security

Data responses default to Cache-Control: private, no-store, so shared caches never store per-user data. Pass { cache: "public, max-age=60" } only for data that is identical for every request.

server.ts modules are server-only. If browser code imports one, the build fails. Keep credentials and database clients in modules that only server.ts imports.

Server data answers GET and HEAD. Forms and mutations are not part of server data yet.

Development

fuzor dev serves data from the same endpoints and reloads server.ts changes. Adding or removing the first server.ts changes the build profile, so restart the dev server.

Built with Fuzor Back to top ↑
On this page Back to top ↑