tanstack-start-ssrlisted
Install: claude install-skill vantionlabs/saas-starter
# A GET is rendered on the server; a write happens on the client
A dashboard that fetches after hydration shows a spinner for work the server could already
have done. So a read a route can name is a **route loader**, and what stays in the browser
is what a person asks for: creating, editing, deleting, and the refetch that follows.
It is one query either way. `contactsAtom` is still what the screen reads, still what a
write invalidates, and still what `apps/mobile` renders with no server behind it — the
loader only arranges for it to arrive already full.
## Three pieces
1. **`server/rpc.ts`** — the RPC client as the server uses it. The cookie is **read inside
`serverRpc`**, never passed in: it was a parameter at every call site once, which made
the one thing that must never be forgotten into one somebody could pass wrong. Only the
cookie goes; a whole header set sends `host` and `content-length`, which describe the
request rather than the caller.
2. **`server/reads/`** — one file per concern. `apps/admin/src/server/` splits the same
way, because one file holding the authentication _and_ every read is the one that grows
a procedure somebody forgot to put `requireStaff` in front of.
3. **The atom** — `Atom.serializable({ key, schema })` declared in `packages/core` beside
the atom, because the server needs the same pair to encode and a pair kept twice drifts
into hydrating nothing while the page quietly fetches again.
## Server functions are GET only