Query cache
For client-side data that isn't a route loader — lists, widgets, anything refetchable — Nifra ships a keyed query cache (TanStack-Query-style), agnostic across React and Solid.
useQuery / createQuery
useQuery(key, fn) (React) and createQuery(key, fn) (Solid) subscribe a component to a cached, keyed query. Concurrent reads of the same key dedup into one in-flight fetch; results are cached with a staleTime, a background refetch, and bounded GC.
import { useQuery, useQueryClient } from "@nifrajs/web-react/query"
function Profile({ id }: { id: string }) {
// Keyed + cached. Concurrent useQuery's with the same key dedup into one fetch;
// results are cached, with staleTime + background refetch.
const { data, isPending, refetch } = useQuery(["user", id], () =>
fetch(`/api/users/${id}`).then((r) => r.json()),
)
if (isPending) return <p>Loading…</p>
return <h1>{data.name}</h1>
}
function CreateUser() {
const qc = useQueryClient()
// After a mutation, invalidate by key (or prefix) — matching queries refetch.
return <button onClick={() => qc.invalidateQueries(["user"])}>Refresh</button>
}Invalidation
After a mutation, useQueryClient().invalidateQueries(key) marks matching entries stale (by exact key or array prefix) and refetches the mounted ones — no manual cache surgery.
It's the same agnostic core under both adapters: the cache, dedup, and invalidation logic live in @nifrajs/web; the bindings are thin useSyncExternalStore / signal wrappers.