Remote / foldkit-remote
If you know TanStack Query, TanStack DB or fate
The same job, done from the other side of the Model:
| TanStack Query | foldkit-remote |
|---|---|
useQuery({ queryKey, queryFn }) in a component | Data.get(Selection, id) in a Surface; the active Surface fetches |
a queryKey you design | the entity, its id and the fields selected; there is no key to design |
| one cache entry per key | one entity per id, shared by every selection of it |
staleTime, refetchOnWindowFocus | RemotePolicy, Data.refresh from update |
gcTime | retention: what no active Surface reaches is collected |
onMutate + setQueryData | optimistic: [Remote.patch(...)], a layer the settlement removes |
invalidateQueries | not needed for overlap: a result updates the one copy |
isPending, isError, data | RemoteData: six states, one exhaustive fold |
a QueryClient beside your state | Remote.Model, a field of your Model, changed only by Messages |
Two differences change how you build. There are no keys. A TanStack cache
is a map from keys you design to results you shape, so two components that
want overlapping data either share a key and over-fetch, or hold two copies
and invalidate by hand. Remote stores fields by entity and id, and a Surface
declares which fields it selects, so overlap is the normal case and a
mutation's result is seen everywhere at once. The cache is Model. A
QueryClient lives beside your state and is updated by effects; Remote.Model
is a field of your Model, changed by the same reducer as everything else, so a
screen is a function of the Model, replays from a recording, and is tested
without a network.
What Remote does not have: a component hook (it has Surfaces and Foldkit
Subscriptions), a devtools panel of its own (Data.inspect and Data.why are
the data one would show), and retries on failure (a failed read stays failed
until something asks again; see When a read fails).
And TanStack DB, and fate
TanStack DB is a local database: collections
populated eagerly (or on demand) from a REST API or a sync engine, live queries
that update incrementally, and optimistic transactions rolled back on failure.
Its unit is the collection; a screen queries what is already local. Remote's
unit is the field: a screen declares which fields of which entities it reads,
and only those are fetched, so there is no collection to load first and a page
carries what it shows. Remote has no live query engine: Data.filtered and
Remote.matching judge the rows a list already holds, and a new list is a
server query. Where TanStack DB syncs client-authored writes through a sync
engine, that job here belongs to foldkit-sync, and Remote stays
the disposable cache of what the server owns.
fate is the nearest cousin: components declare views of exactly the data they need, composed into one request per screen, with a normalized cache that masks what a component did not ask for, and actions with optimistic rollback. Remote's Selections are that strictness, and its store is that cache. The differences are where it lives and what runs it: fate is a React data client, driven by Suspense and Actions; Remote is a Submodel of a Foldkit Model, driven by which Surfaces are active, with no view framework in it.