The framework agnostic core that powers TanStack Query for data fetching and caching
—
Low-level cache operations for direct query and mutation cache manipulation with events, filtering, batch operations, and advanced cache strategies.
Low-level cache for managing query instances and their lifecycle.
/**
* Cache for managing Query instances
* Provides direct access to query storage and lifecycle management
*/
class QueryCache {
constructor(config?: QueryCacheConfig);
/**
* Build or retrieve a query from the cache
* Creates new query if it doesn't exist, otherwise returns existing
* @param client - QueryClient instance
* @param options - Query options
* @param state - Optional initial state
* @returns Query instance
*/
build<T>(client: QueryClient, options: QueryOptions<T>, state?: QueryState<T>): Query<T>;
/**
* Add a query to the cache
* @param query - Query instance to add
*/
add(query: Query): void;
/**
* Remove a query from the cache
* @param query - Query instance to remove
*/
remove(query: Query): void;
/**
* Clear all queries from the cache
*/
clear(): void;
/**
* Get a query by its hash
* @param queryHash - The query hash to look up
* @returns Query instance or undefined if not found
*/
get<T>(queryHash: string): Query<T> | undefined;
/**
* Get all queries in the cache
* @returns Array of all Query instances
*/
getAll(): Array<Query>;
/**
* Find the first query matching the filters
* @param filters - Query filters to match against
* @returns First matching Query instance or undefined
*/
find<T>(filters: QueryFilters): Query<T> | undefined;
/**
* Find all queries matching the filters
* @param filters - Query filters to match against
* @returns Array of matching Query instances
*/
findAll(filters?: QueryFilters): Array<Query>;
/**
* Subscribe to cache events
* @param callback - Callback function for cache events
* @returns Unsubscribe function
*/
subscribe(callback: (event: QueryCacheNotifyEvent) => void): () => void;
/**
* Notify cache listeners of an event
* @param event - Cache event to broadcast
*/
notify(event: QueryCacheNotifyEvent): void;
/**
* Handle window focus event
* Triggers refetch for queries configured to refetch on focus
*/
onFocus(): void;
/**
* Handle online event
* Triggers refetch for queries configured to refetch on reconnect
*/
onOnline(): void;
}
interface QueryCacheConfig {
/**
* Called when any query encounters an error
* @param error - The error that occurred
* @param query - The query that failed
*/
onError?: (error: Error, query: Query) => void;
/**
* Called when any query succeeds
* @param data - The data returned by the query
* @param query - The query that succeeded
*/
onSuccess?: (data: unknown, query: Query) => void;
/**
* Called when any query settles (success or error)
* @param data - The data returned (undefined if error)
* @param error - The error that occurred (null if success)
* @param query - The query that settled
*/
onSettled?: (data: unknown | undefined, error: Error | null, query: Query) => void;
}Usage Examples:
import { QueryCache, QueryClient } from "@tanstack/query-core";
// Create cache with event handlers
const queryCache = new QueryCache({
onError: (error, query) => {
console.log(`Query failed:`, query.queryKey, error);
// Log errors to monitoring service
errorReporting.captureException(error, {
tags: { queryKey: JSON.stringify(query.queryKey) },
});
},
onSuccess: (data, query) => {
console.log(`Query succeeded:`, query.queryKey);
},
onSettled: (data, error, query) => {
console.log(`Query settled:`, query.queryKey, { data, error });
},
});
const queryClient = new QueryClient({ queryCache });
// Subscribe to cache events
const unsubscribe = queryCache.subscribe((event) => {
switch (event.type) {
case 'added':
console.log('Query added:', event.query.queryKey);
break;
case 'removed':
console.log('Query removed:', event.query.queryKey);
break;
case 'updated':
console.log('Query updated:', event.query.queryKey, event.action);
break;
case 'observerAdded':
console.log('Observer added to query:', event.query.queryKey);
break;
case 'observerRemoved':
console.log('Observer removed from query:', event.query.queryKey);
break;
}
});
// Find queries by filters
const staleQueries = queryCache.findAll({ stale: true });
console.log(`Found ${staleQueries.length} stale queries`);
const userQueries = queryCache.findAll({
queryKey: ['user'],
exact: false, // Partial match
});
// Get specific query
const userQuery = queryCache.find({ queryKey: ['user', 123], exact: true });
if (userQuery) {
console.log('User query state:', userQuery.state);
}
// Clear cache
queryCache.clear();
// Cleanup
unsubscribe();Low-level cache for managing mutation instances and their execution.
/**
* Cache for managing Mutation instances
* Handles mutation storage, scoping, and execution coordination
*/
class MutationCache {
constructor(config?: MutationCacheConfig);
/**
* Build or retrieve a mutation from the cache
* @param client - QueryClient instance
* @param options - Mutation options
* @param state - Optional initial state
* @returns Mutation instance
*/
build<T>(client: QueryClient, options: MutationOptions<T>, state?: MutationState<T>): Mutation<T>;
/**
* Add a mutation to the cache
* @param mutation - Mutation instance to add
*/
add(mutation: Mutation): void;
/**
* Remove a mutation from the cache
* @param mutation - Mutation instance to remove
*/
remove(mutation: Mutation): void;
/**
* Clear all mutations from the cache
*/
clear(): void;
/**
* Get all mutations in the cache
* @returns Array of all Mutation instances
*/
getAll(): Array<Mutation>;
/**
* Find the first mutation matching the filters
* @param filters - Mutation filters to match against
* @returns First matching Mutation instance or undefined
*/
find<T>(filters: MutationFilters): Mutation<T> | undefined;
/**
* Find all mutations matching the filters
* @param filters - Mutation filters to match against
* @returns Array of matching Mutation instances
*/
findAll(filters?: MutationFilters): Array<Mutation>;
/**
* Subscribe to cache events
* @param callback - Callback function for cache events
* @returns Unsubscribe function
*/
subscribe(callback: (event: MutationCacheNotifyEvent) => void): () => void;
/**
* Notify cache listeners of an event
* @param event - Cache event to broadcast
*/
notify(event: MutationCacheNotifyEvent): void;
/**
* Check if a mutation can run (respects scoping)
* @param mutation - Mutation to check
* @returns true if mutation can run
*/
canRun(mutation: Mutation): boolean;
/**
* Run the next mutation in the scope queue
* @param mutation - Mutation that just completed
* @returns Promise that resolves when next mutation runs
*/
runNext(mutation: Mutation): Promise<unknown>;
/**
* Resume all paused mutations
* @returns Promise that resolves when all mutations are resumed
*/
resumePausedMutations(): Promise<unknown>;
}
interface MutationCacheConfig {
/**
* Called when any mutation encounters an error
* @param error - The error that occurred
* @param variables - Variables passed to the mutation
* @param context - Context from onMutate
* @param mutation - The mutation that failed
*/
onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown;
/**
* Called when any mutation succeeds
* @param data - The data returned by the mutation
* @param variables - Variables passed to the mutation
* @param context - Context from onMutate
* @param mutation - The mutation that succeeded
*/
onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown;
/**
* Called when any mutation settles
* @param data - The data returned (undefined if error)
* @param error - The error that occurred (null if success)
* @param variables - Variables passed to the mutation
* @param context - Context from onMutate
* @param mutation - The mutation that settled
*/
onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown;
}Usage Examples:
import { MutationCache, QueryClient } from "@tanstack/query-core";
// Create cache with global mutation handlers
const mutationCache = new MutationCache({
onError: (error, variables, context, mutation) => {
console.error('Mutation failed:', mutation.options.mutationKey, error);
// Global error handling
if (error.status === 401) {
// Redirect to login
window.location.href = '/login';
}
},
onSuccess: (data, variables, context, mutation) => {
console.log('Mutation succeeded:', mutation.options.mutationKey);
// Global success tracking
analytics.track('mutation_success', {
mutationKey: mutation.options.mutationKey,
});
},
});
const queryClient = new QueryClient({ mutationCache });
// Subscribe to mutation events
const unsubscribe = mutationCache.subscribe((event) => {
switch (event.type) {
case 'added':
console.log('Mutation added:', event.mutation.mutationId);
break;
case 'removed':
console.log('Mutation removed:', event.mutation.mutationId);
break;
case 'updated':
console.log('Mutation updated:', event.mutation.mutationId, event.action);
break;
}
});
// Find pending mutations
const pendingMutations = mutationCache.findAll({
status: 'pending'
});
console.log(`${pendingMutations.length} mutations pending`);
// Check mutation queue status
const allMutations = mutationCache.getAll();
const queuedMutations = allMutations.filter(m => !mutationCache.canRun(m));
console.log(`${queuedMutations.length} mutations queued`);
// Resume paused mutations (e.g., after network reconnection)
await mutationCache.resumePausedMutations();
// Cleanup
unsubscribe();Understanding cache events for advanced cache monitoring and debugging.
type QueryCacheNotifyEvent =
| QueryCacheNotifyEventAdded
| QueryCacheNotifyEventRemoved
| QueryCacheNotifyEventUpdated
| QueryCacheNotifyEventObserverAdded
| QueryCacheNotifyEventObserverRemoved
| QueryCacheNotifyEventObserverOptionsUpdated
| QueryCacheNotifyEventObserverResultsUpdated;
interface QueryCacheNotifyEventAdded {
type: 'added';
query: Query;
}
interface QueryCacheNotifyEventRemoved {
type: 'removed';
query: Query;
}
interface QueryCacheNotifyEventUpdated {
type: 'updated';
query: Query;
action: Action;
}
interface QueryCacheNotifyEventObserverAdded {
type: 'observerAdded';
query: Query;
observer: QueryObserver;
}
interface QueryCacheNotifyEventObserverRemoved {
type: 'observerRemoved';
query: Query;
observer: QueryObserver;
}
type MutationCacheNotifyEvent =
| MutationCacheNotifyEventAdded
| MutationCacheNotifyEventRemoved
| MutationCacheNotifyEventUpdated;
interface MutationCacheNotifyEventAdded {
type: 'added';
mutation: Mutation;
}
interface MutationCacheNotifyEventRemoved {
type: 'removed';
mutation: Mutation;
}
interface MutationCacheNotifyEventUpdated {
type: 'updated';
mutation: Mutation;
action: Action;
}Usage Examples:
// Advanced cache monitoring
const queryCache = new QueryCache();
queryCache.subscribe((event) => {
// Track query lifecycle
if (event.type === 'added') {
console.log(`New query created: ${JSON.stringify(event.query.queryKey)}`);
// Track memory usage
const totalQueries = queryCache.getAll().length;
console.log(`Total queries in cache: ${totalQueries}`);
}
if (event.type === 'updated') {
const { query, action } = event;
// Monitor specific query state changes
if (action.type === 'success') {
console.log(`Query succeeded: ${JSON.stringify(query.queryKey)}`);
console.log(`Data size: ${JSON.stringify(query.state.data).length} bytes`);
}
if (action.type === 'error') {
console.error(`Query failed: ${JSON.stringify(query.queryKey)}`, action.error);
}
}
if (event.type === 'observerAdded') {
console.log(`Observer added to: ${JSON.stringify(event.query.queryKey)}`);
console.log(`Total observers: ${event.query.observers.length}`);
}
});Advanced filtering options for finding specific queries and mutations.
interface QueryFilters {
/** Query key to match (exact or partial based on 'exact' flag) */
queryKey?: QueryKey;
/** Whether to match query key exactly */
exact?: boolean;
/** Filter by stale status */
stale?: boolean;
/** Filter by active status (has observers) */
active?: boolean;
/** Filter by inactive status (no observers) */
inactive?: boolean;
/** Filter by fetch status */
fetchStatus?: FetchStatus;
/** Filter by query status */
status?: QueryStatus;
/** Custom predicate function */
predicate?: (query: Query) => boolean;
/** Filter by query type */
type?: QueryTypeFilter;
}
interface MutationFilters {
/** Mutation key to match (exact or partial based on 'exact' flag) */
mutationKey?: MutationKey;
/** Whether to match mutation key exactly */
exact?: boolean;
/** Filter by mutation status */
status?: MutationStatus;
/** Custom predicate function */
predicate?: (mutation: Mutation) => boolean;
}
type QueryTypeFilter = 'all' | 'active' | 'inactive';
type FetchStatus = 'fetching' | 'paused' | 'idle';
type QueryStatus = 'pending' | 'error' | 'success';
type MutationStatus = 'idle' | 'pending' | 'success' | 'error';Usage Examples:
// Complex filtering examples
const queryCache = new QueryCache();
// Find all stale user queries
const staleUserQueries = queryCache.findAll({
queryKey: ['user'],
exact: false,
stale: true,
});
// Find queries that are currently fetching
const fetchingQueries = queryCache.findAll({
fetchStatus: 'fetching',
});
// Find inactive queries older than 1 hour
const oldInactiveQueries = queryCache.findAll({
inactive: true,
predicate: (query) => {
const oneHourAgo = Date.now() - 60 * 60 * 1000;
return query.state.dataUpdatedAt < oneHourAgo;
},
});
// Custom cleanup based on filters
const cleanupOldQueries = () => {
const queriesToRemove = queryCache.findAll({
predicate: (query) => {
// Remove queries older than 24 hours with no observers
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
return query.observers.length === 0 &&
query.state.dataUpdatedAt < oneDayAgo;
},
});
queriesToRemove.forEach(query => {
queryCache.remove(query);
});
console.log(`Cleaned up ${queriesToRemove.length} old queries`);
};
// Run cleanup periodically
setInterval(cleanupOldQueries, 60 * 60 * 1000); // Every hourinterface QueryOptions<T> {
queryKey: QueryKey;
queryFn?: QueryFunction<T>;
enabled?: boolean;
networkMode?: NetworkMode;
retry?: RetryValue<T>;
retryDelay?: RetryDelayValue<T>;
staleTime?: number;
gcTime?: number;
meta?: QueryMeta;
initialData?: T;
initialDataUpdatedAt?: number;
structuralSharing?: boolean;
}
interface MutationOptions<T> {
mutationKey?: MutationKey;
mutationFn?: MutationFunction<T>;
retry?: RetryValue<T>;
retryDelay?: RetryDelayValue<T>;
networkMode?: NetworkMode;
meta?: MutationMeta;
scope?: { id: string };
}
type QueryKey = ReadonlyArray<unknown>;
type MutationKey = ReadonlyArray<unknown>;
type NetworkMode = 'online' | 'always' | 'offlineFirst';Install with Tessl CLI
npx tessl i tessl/npm-tanstack--query-core