Query
@domphy/query provides async state management for Domphy apps: fetching, caching, deduplication, background refetching, mutations, infinite queries, and SSR hydration.
It is a 1-1 port of @tanstack/query-core v5.90.20 (MIT, © Tanner Linsley and the TanStack team). The source is kept byte-identical to upstream, so the entire TanStack Query core reference applies as-is, and future upstream versions can be diffed and merged directly.
Like the rest of Domphy, it is framework-agnostic and has zero dependencies — the bridge to the UI is plain toState.
Install
npm install @domphy/query
The CDN bundle exposes Domphy.query with all exports.
Live Example
Core Concepts
QueryClient— owns theQueryCacheandMutationCache. Create one per app, callqueryClient.mount()once so window focus and reconnect refetching work.QueryObserver— subscribes to one query: runs thequeryFn, caches the result underqueryKey, and emits a result object on every state change.MutationObserver— runs create/update/delete operations with retry and lifecycle callbacks.- Query keys — arrays like
["users", userId]. Hashed structurally, so object key order does not matter. Keys are also the handle for invalidation and prefetching.
The Bridge Pattern
TIP
Most apps should use the Domphy adapter (createQuery / createMutation / createInfiniteQuery) — it packages the pattern below into reactive accessors, and the live example above already uses it. Read on to understand what the adapter does under the hood.
Domphy has no async primitive by design — async is a state problem, and state lives outside the UI. @domphy/query manages the async state; toState pushes results into the UI:
import { QueryClient, QueryObserver } from "@domphy/query"
import { toState } from "@domphy/core"
const queryClient = new QueryClient()
queryClient.mount()
const data = toState<User[] | undefined>(undefined)
const loading = toState(true)
const observer = new QueryObserver<User[]>(queryClient, {
queryKey: ["users"],
queryFn: () => fetch("/api/users").then((res) => res.json()),
})
observer.subscribe((result) => {
data.set(result.data)
loading.set(result.isPending)
})
The UI reads the states reactively — nothing query-specific leaks into elements:
const App: DomphyElement<"ul"> = {
ul: (l) => (data.get(l) ?? []).map((user) => ({
li: user.name,
_key: user.id,
})),
hidden: (l) => loading.get(l),
}
What To Read Next
- Domphy Adapter for
createQuery/createMutation/createInfiniteQuery— the recommended way to consume queries - Queries for
QueryObserver, options, and a reusablecreateQueryhelper - Mutations for writes, callbacks, and optimistic updates
- Caching for invalidation, prefetching, and the
staleTime/gcTimemodel - Infinite Queries for pagination
- SSR & Hydration for server rendering with Domphy SSR
- API Reference for the full export list