Server API
These modules run in Node. Endpoints and server data modules import helpers from fuzor/server; deployments use fuzor/server/node.
fuzor/server
Endpoint helpers
Used in api/ files; see API endpoints.
| Export | Meaning |
|---|---|
ApiContext (type) | { request, url, params, signal } |
ApiError(status, message, headers?) | Throw or fail with it to answer with that status and { "error": message } |
readBody(context, schema) | Effect decoding a JSON or form body with a Schema; fails with ApiError 400, 413 or 415 |
json(value, init?) | JSON Response with Cache-Control: no-store unless set |
redirect(location, context, status?) | Redirect Response, 303 by default |
ServerRequest | Effect service providing the ApiContext in Effect handlers and loaders |
RequestBodyTooLargeError | Raised while reading a body over the server limit |
apiMethods | The supported methods |
Cookies and sessions
See API endpoints.
| Export | Meaning |
|---|---|
getCookies(request), getCookie(request, name) | Parsed request cookies |
setCookie(responseOrHeaders, name, value, options?, context?) | Appends a Set-Cookie header; defaults HttpOnly, SameSite=Lax, Path=/, Secure except on local HTTP when context is passed |
deleteCookie(responseOrHeaders, name, options?, context?) | Expires a cookie |
serializeCookie(name, value, options?, context?) | The header value |
signValue(value, secret), verifySignedValue(signed, secrets) | HMAC-SHA256 signing with secret rotation |
sessionCookie({ name, secrets, maxAgeSeconds, ...cookieOptions }) | { read(context), write(response, data, context?), clear(response, context?) } for signed, expiring JSON sessions |
Server data
Used in pages/**/server.ts; see server data.
| Export | Meaning |
|---|---|
defineServerData(loader, options?) | Declares the page's loader. options.cache (default private, no-store), options.timeoutMs |
notFound() | Return from a loader to answer 404 |
ServerDataError | A failed loader, passed to onError |
Handler
createServerHandler(manifest, options?) returns (request: Request) => Promise<Response>, the portable core of every server build. Generated server/entry.mjs files export manifest and handler. Options: data and api (filled in by the generated entry) and onError.
The handler answers, in order: API endpoints below /api, server data below /_fuzor/data, redirects, then prerendered documents with ETags and the nearest not-found page. Unexpected failures return the compiled error page with status 500.
Because it uses Web Request and Response, the handler can run on any server that speaks the Fetch API; the Node adapter below adds static assets, request bodies and shutdown handling.
fuzor/server/node
| Export | Meaning |
|---|---|
createNodeServer(options) | A Node http.Server that calls handler and serves files from assetsDir for URLs the handler does not know. Options: handler, assetsDir, base, onError, maxBodyBytes (default 1 MiB). |
createStaticHandler({ root, target, base? }) | A handler that serves a static build like a static host; fuzor preview uses it |
closeNodeServer(server, { timeoutMs? }) | Stops accepting connections, finishes in-flight requests, then closes remaining sockets after timeoutMs (default 10 s) |
handleShutdownSignals(server, options?) | Calls closeNodeServer on SIGINT and SIGTERM |
toWebRequest(incoming, { url, signal, maxBodyBytes? }) | Converts a Node request, streaming its body |
createNodeServer also accepts onResponse(info), called once per response with { method, url, status, durationMs, aborted }; formatResponse(info) turns it into a log line such as GET /api/status 200 3ms.
A complete production server for a server build:
// server.mjs
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createNodeServer, handleShutdownSignals } from "fuzor/server/node";
import { handler, manifest } from "./dist-mpa-server/server/entry.mjs";
const here = path.dirname(fileURLToPath(import.meta.url));
const server = createNodeServer({ handler, base: manifest.base, assetsDir: path.join(here, "dist-mpa-server/client") });
handleShutdownSignals(server);
server.listen(Number(process.env.PORT ?? 3000), process.env.HOST ?? "0.0.0.0");
Hashed files in assets/ are served as immutable; other files revalidate. Paths that leave assetsDir, including through symlinks, are refused.
fuzor/vite
| Export | Meaning |
|---|---|
fuzorPlugin(options?) | The Vite plugin; see configuration |
resolveFuzorProfile(options, env?, serverModules?) | How the plugin resolves target, server, inline and the output folder |
fuzor/compiler
For tools that need compiled routes without Vite:
| Export | Meaning |
|---|---|
compileProject(root, options?) | Promise of { routes, boundaries, redirects, files, warnings }. Options: markdown, redirects, loadModule (for paths modules; the default uses Node import()), signal, cache. |
CompileCache | Pass the same instance to repeated compileProject calls to reuse unchanged pages; entries are checked against the components and layouts each page read. stats() returns { hits, misses, entries } for the latest compilation. fuzor dev uses one automatically. |
compileProjectEffect(root, options?) | The same as an Effect requiring CompilerFileSystem |
CompilerFileSystem, CompilerFileSystemLive | File access service and its Node Layer |
CompilerError | Typed compilation failure. location holds { file, line?, column? }, found in the page, layout or component that contains the problem; Vite shows it with a code frame. |
compileMarkdown(source, options?) | Markdown to { html, headings, toc, metadata }; options.elements allows components on their own lines |
resolveRedirects(redirects, routes) | Validates and collapses a redirect table |
compileProject does not render island previews; that step needs Vite and runs inside the plugin.
fuzor
The package root exports fuzorPlugin, the compiler functions above, and Effect and Schema namespaces for convenience. In browser bundles it exports only Effect and Schema.