Skip to main content

QueryClient

QueryClient owns the caches and everything imperative: fetching, reading and writing cached data, invalidating, cancelling. Create one per app (or one per test) and keep it for as long as the app runs — the cache lives in it. In Flutter it goes into a QueryClientProvider, which mounts it; in pure Dart you call mount() yourself (see without Flutter).

A typical setup, in lib/main.dart, with a cache-wide error hook for the app's logger:

final QueryClient appClient = QueryClient(
queryCache: QueryCache(
onError: (error, stackTrace, query) => log.warning(
'query ${query.queryKey.debugString} failed',
error,
stackTrace,
),
),
defaultOptions: const DefaultOptions(
queries: QueryDefaults(
staleTime: StaleTime.duration(Duration(seconds: 30)),
),
),
);

Every method that takes filters takes them as a named filters: argument, a QueryFilters (or MutationFilters). The empty filter matches everything.

Where a member's TanStack Query name differs from the Dart one, the table says so at the end of the row; a member without such a note has the same name in TanStack Query.

Constructor and fields​

MemberTypeDefaultWhat it is
QueryClient({queryCache, mutationCache, defaultOptions, focusManager, onlineManager, notifyManager})a fresh instance of each; defaultOptions emptyEvery collaborator is optional. Pass your own caches to install cache-wide callbacks. TanStack: new QueryClient(config), whose config takes no managers.
queryCacheQueryCacheEvery query, keyed by QueryKey. Subscribe to it for cache events. TanStack: getQueryCache().
mutationCacheMutationCacheEvery mutation, in submission order. TanStack: getMutationCache().
focusManagerAppFocusManagerWhether the app is in the foreground. The Flutter binding drives it from the app lifecycle. TanStack: the module-level focusManager.
onlineManagerOnlineManagerWhether the device is believed online. The binding drives it from QueryClientProvider.onlineStatus. TanStack: the module-level onlineManager.
notifyManagerNotifyManagerBatches this client's listener notifications. Pass NotifyManager.shared to batch across clients. TanStack: the module-level notifyManager.

The three managers belong to the client rather than to the module, so two clients — two tests — never share focus, connectivity or batching state.

Fetching​

MethodReturnsWhat it does
query<T>(options, {revalidateIfStale = false})Future<T>Takes a QueryOptions<T>. Returns cached data if it is fresh under the options' staleTime; otherwise fetches (or joins the fetch in flight), caches and completes with the data. A failed fetch fails the future. No retries unless retry is set on the options or in the defaults. With revalidateIfStale, cached data is returned at once while a stale query refreshes in the background, and the future fails only when nothing is cached. When the call creates the entry or starts a fetch, the options become the query's options (with retry unset, the query keeps its own); served from the cache or joining a fetch in flight, they are not applied.
infiniteQuery<P, Q>(options, {revalidateIfStale = false})Future<InfiniteData<P, Q>>The same for an InfiniteQueryOptions<P, Q>: the first page, or options.pages pages.
observe<TQueryData, TData>(options)QueryObserver<TQueryData, TData>Creates an observer on this client — the same as QueryObserver(client, options). It does nothing until subscribed; the caller owns it. TanStack: new QueryObserver(client, options).
observeInfinite<P, Q, TData>(options)InfiniteQueryObserver<P, Q, TData>The infinite twin of observe. TanStack: new InfiniteQueryObserver(client, options).

TanStack Query's query and infiniteQuery replace its deprecated fetchQuery, prefetchQuery and ensureQueryData (and their infinite twins); this package has only the new pair. To prefetch, .ignore() the future; to fetch only when nothing is cached, pass staleTime: StaleTime.static; to reshape the result, await and map it — there is no select here. See prefetching.

Reading​

MethodReturnsWhat it does
getQueryData<T>(key)T?The cached data, stale or not, or null. Neither fetches nor subscribes. Throws QueryDataTypeError if the entry holds another type.
getInfiniteQueryData<P, Q>(key)InfiniteData<P, Q>?getQueryData for an infinite query. TanStack: getQueryData.
getQueryState<T>(key)QueryState<T>?The entry's whole state: status, fetch status, timestamps, counters. Throws QueryDataTypeError on a type mismatch.
getQueriesData<T>({required filters})List<(QueryKey, T?)>The data of every matching entry, null where one holds none yet. Throws QueryDataTypeError if any match holds another type.
isFetching({filters})intHow many matching queries are fetching right now (not paused). A fetchStatus in the filters is ignored.
isMutating({filters})intHow many matching mutations are pending, callbacks included. A status in the filters is ignored. Takes a MutationFilters.

Writing​

MethodReturnsWhat it does
setQueryData<T>(key, data, {updatedAt})TWrites data, creating the entry if needed; it counts as freshly fetched (updatedAt, default now). An existing entry takes any value its type can hold; otherwise QueryDataTypeError. Returns what the cache now holds, after structural sharing. A bare setQueryData(key, null) writes nothing; name the nullable type to store null. TanStack: setQueryData(key, value).
updateQueryData<T>(key, updater, {updatedAt})T?updater receives the current data (or null) and returns the new data; returning null writes nothing and returns null. Throws QueryDataTypeError when the held data is not a T or the entry cannot hold what the updater returned. TanStack: setQueryData(key, updater).
updateQueriesData<T>(updater, {required filters, updatedAt})List<(QueryKey, T?)>The same over every match, in one batch. Every updater runs, and every type is checked, before anything is written, so an updater that reads another matched key sees it unwritten. TanStack: setQueriesData.

See updates from mutation responses and optimistic updates.

Operating on many queries​

All five take filters (default: every query). The four that return a future report a throwing filter predicate by failing the future, and their refetch failures land in the queries' states, not in the future.

MethodReturnsWhat it does
invalidateQueries({filters, refetchType, cancelRefetch = true})Future<void>Marks the matches stale, then refetches those refetchType names — a RefetchType (active, inactive, all, none); unset, the filter's own type, else the active ones. RefetchType.none only marks. The matched set is fixed before marking.
refetchQueries({filters, cancelRefetch = true})Future<void>Refetches the matches, stale or not. Skipped: a query none of whose observers is enabled, an unobserved query that has never fetched, and one an observer marks StaleTime.static. Does not wait for a fetch paused for the network.
cancelQueries({filters, revert = true, silent = false})Future<void>Cancels matching fetches in flight and completes when they have settled. revert puts each query back to its state before the fetch; silent cancels without dispatching an error. A failed cancellation never fails the future.
resetQueries({filters, cancelRefetch = true})Future<void>Puts the matches back to their initial state (initial data included), then refetches the active ones.
removeQueries({filters})voidRemoves the matches from the cache, cancelling their fetches silently. For keys nobody watches; an observer stays on the removed entry.

cancelRefetch: true restarts a fetch already in flight on a query that holds data, and joins it on one that does not; false always joins. See query invalidation and filters.

Defaults​

MemberReturnsWhat it does
getDefaultOptions()DefaultOptionsThe client-wide defaults in force.
setDefaultOptions(options)voidReplaces them. Takes effect wherever options are resolved next; options a query already holds are not changed.
setQueryDefaults(key, defaults)voidDefaults for every query whose key starts with key. The same key again replaces them.
getQueryDefaults(key)QueryDefaults?Every registration matching key, merged in the order the keys were first registered (registering a key again replaces its defaults but keeps its place), the later winning per field; null when none matches. The client-wide defaults are not included.
setMutationDefaults(key, defaults)voidThe mutation twin. Carries no callbacks.
getMutationDefaults(key)MutationDefaults?The mutation twin of getQueryDefaults.
defaultQueryOptions(options)DefaultedQueryOptions<T>The options with every unset field filled in: what a query actually runs with. Throws ArgumentError when both initialDataUpdatedAt forms are set. Rarely needed outside a test.
defaultQueryObserverOptions(options)DefaultedQueryObserverOptions<TQueryData, TData>The same for observer options, observer fields included. TanStack: defaultQueryOptions.
defaultMutationOptions(options)DefaultedMutationOptions<TData, TVariables, TOnMutateResult>The same for mutation options. Throws ArgumentError when both mutationFn and mutationFnWithContext are set.
infiniteObserverOptions(options)QueryObserverOptionsBase<InfiniteData<P, Q>, TData>Turns infinite observer options into the options an InfiniteQueryObserver.setOptions takes; setInfiniteOptions on the observer is the shorter way. No TanStack counterpart.

The three defaults classes:

ClassFields
DefaultOptionsqueries (QueryDefaults?), mutations (MutationDefaults?).
QueryDefaultsqueryFn, structuralSharing, enabled, staleTime, gcTime, retry, retryDelay, retryOnMount, networkMode, refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval, refetchIntervalInBackground, meta. TanStack: DefaultOptions.queries.
MutationDefaultsmutationFn, retry, retryDelay, networkMode, gcTime, scope, meta. TanStack: DefaultOptions.mutations, which also takes callbacks.

Each field means what the option of the same name means. queryFn, structuralSharing and mutationFn are typed on Object?, because one default serves every type under a key prefix; the result is checked against the reader's type and a mismatch throws QueryDataTypeError. initialData, placeholderData and select belong to one query and have no default. All three classes are const, compare by value (functions by ==, so a closure equals only itself), and QueryDefaults and MutationDefaults have mergedWith(other), which lays other's set fields over these. See default query function.

Lifecycle​

MethodWhat it does
mount()Starts listening to focus and connectivity: focus and reconnect refetches, resuming paused mutations (first, and the queries wait for them). Counted: two mounts need two unmounts. QueryClientProvider mounts its client.
unmount()Stops listening. Does not touch the caches. An unmount without a mount does nothing.
clear()Empties both caches, cancelling fetches and every gcTime timer. Observers are not stopped — destroy them first. A pending mutation dropped here fails, and its callbacks run a few microtasks later; see testing for the teardown that allows for it.
resumePausedMutations()Returns a Future<void>. Resumes every paused mutation that can run now and completes when they have settled. The gate is each mutation's own network mode; TanStack Query instead skips the whole call while offline.

Not here​

getQueryCache() and getMutationCache() are fields, and the deprecated fetchQuery, prefetchQuery and ensureQueryData family is covered by query. Persistence (hydrate, dehydrate), isRestoring and queryKeyHashFn are not in 1.0; see the feature matrix.