Foundations · for everyone

000. What fsr is made of

The question this chapter answers: what is fsr, who is it for, what does it refuse to do and what do its terms mean in terms you already know?

Two languages, one application

An fsr application is written in TypeScript and run by Rust. Most of the design follows from that split, so here is what it means in practice.

The TypeScript is the application: the routes, the data each page needs, the actions a user can take, the pages and the components they are built from. It lives under app/ and it is the thing a team changes every day. The Rust is the runtime: matching the request, fetching the data, rendering the page, holding the session, talking to the services. It lives in crates and it is the thing a team changes when the platform needs to grow. A platform developer can take any single piece of the application back into Rust without the rest noticing, which chapter 201 covers. That is unusual in practice.

What makes this different from a Node framework is what is missing. There is no JavaScript engine in the serving path. There is no node_modules. There is no npm install. The TypeScript is read at build time, understood and turned into data the Rust runtime executes directly. A loader that fetches products becomes a row in a plan file; a page becomes a render tree; an action becomes a small program the interpreter runs. The build tells you by line when it could not do that and what happens instead.

The two artifacts

Two files describe an application completely. Everything else is generated from them.

The contract is the typed description of every service the application talks to and every schema it declares. It is imported from what the services already publish, an OpenAPI document or a .proto file, plus the interfaces the application writes under schemas/. TypeScript types are generated from it for the editor. The runtime checks every call's arguments and every response against it. Nobody writes a client, which chapter 001 explains.

The plan file is what the build makes of app/: the routes, the lowered bodies, the declared actions and the render trees, in one file under generated/. The host reads it at boot and binds every row to something that answers it. Rust can take a row over, but the file always says who answers. The boot report prints it.

The boundary between the two halves is defined by these two files rather than by either side's code, so neither half can drift from the other without an artifact changing.

What fsr refuses to do

It will not run JavaScript on the server. A body the build cannot understand is residue. The report names the line. Residue in a loader or action stops the build. Residue in a component means that component renders in the browser only. Chapter 002 covers it.

It will not let you write a client. Services are described, imported and called by name through one typed registry. The transport, HTTP or gRPC, is the host's business.

It will not fetch from the internet at runtime. Browser dependencies are vendored into the repository by fsr add and served from disk. A checkout runs with no network and no package manager.

It will not decide policy for you. Which sessions expire, who may act, what an error page says: the mechanisms are there and the defaults are deliberately plain. The report tells you what was inferred, so you can see every decision it made.

The vocabulary map

fsr's words are short and specific. Here is each one beside what other frameworks call it.

fsr saysMeaningElsewhere
bodya loader or an action, the code that runs on the server for a routeloader, server action, API handler
sourcethe named data a route's loader producesloader data, getServerSideProps
actiona named mutation the browser can call, declared and typedserver action, form action, mutation
lowereda body the build turned into data the Rust runtime executescompiled, "runs on the edge"
residuea body or component the build could not lower, named by line"needs a runtime"
contractthe typed description of every service and schemaOpenAPI, the API client's types
plan filethe build's output: routes, bodies, actions and render treesthe route manifest
templatea page, a layout or a component the build lowers and the server renders; not React, whatever it is written againstserver component
pagea route's component, mounted in the browserpage component
statica template with no state and no handlers, so it has no browser twin and nothing bundles itnone: every framework ships the component
islanda component the browser mounts, with or without server markup over itisland, client component
shellthe document around every page: head, import map, entry scriptroot layout, _document
slotwhere a child's content lands in its parentoutlet, children
segmentone region of a page with an identity, so navigation can keep or replace itroute segment
the reportwhat the build and the host print: every name and who answers itnothing quite like it

The word to remember is lowered. Lowering does not emit code. The build reads a body and represents it as data. A loader that awaits two service calls and returns an object is two rows and a shape. The interpreter runs the rows rather than your TypeScript, which is why there is no engine and why the same body reads the same in Rust.

How to read this guide

Each chapter opens with the question it answers and who it is for and ends with a lab: something to do to the running example that makes the chapter's claim observable. The example is shopping_react_ts, a storefront with a catalog, a cart and two backends it does not own, one over HTTP and one over gRPC. Have it running before you start, which its README makes a four-command job.

The labs mostly use the report. The build and the host print the same table, every route, source, action, rendered module and service with who answers it. Most of what this guide claims can be checked by changing something and reading the table again.

The lab

Run the example and read the boot report from top to bottom. Every row is a name the application declared and the thing that answers it. Then open app/generated/plan.sexp and find the same names. The report is the plan file with the host's decisions added, so everything on the screen comes from one of the two artifacts.

Built with SnapFire FSR. Pure Rust runtime, zero Node.js on the server.

Proudly Created by Excerion Sun LLC