CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tanstack--query-core

The framework agnostic core that powers TanStack Query for data fetching and caching

Pending
Overview
Eval results
Files

utilities.mddocs/

Utilities

Core utility functions for key hashing, data manipulation, query matching, functional programming patterns, and advanced data management operations.

Capabilities

Key Utilities

Functions for working with query and mutation keys.

/**
 * Create a stable hash from a query or mutation key
 * Used internally for cache key generation and comparison
 * @param key - Query or mutation key to hash
 * @returns Stable string hash
 */
function hashKey(key: QueryKey | MutationKey): string;

/**
 * Check if two query keys match partially
 * Used for flexible query matching in filters
 * @param a - First query key
 * @param b - Second query key
 * @returns true if keys match partially
 */
function partialMatchKey(a: QueryKey, b: QueryKey): boolean;

Usage Examples:

import { hashKey, partialMatchKey } from "@tanstack/query-core";

// Hash keys for comparison
const hash1 = hashKey(['user', 123]);
const hash2 = hashKey(['user', 123]);
console.log(hash1 === hash2); // true

// Partial key matching
const matches1 = partialMatchKey(['user'], ['user', 123]); // true
const matches2 = partialMatchKey(['user', 123], ['user']); // false
const matches3 = partialMatchKey(['user', 123], ['user', 123]); // true

// Use in custom filtering
const findUserQueries = (userId?: number) => {
  const queries = queryClient.getQueryCache().getAll();
  return queries.filter(query => {
    if (userId) {
      return partialMatchKey(['user', userId], query.queryKey);
    }
    return partialMatchKey(['user'], query.queryKey);
  });
};

Query Matching

Functions for matching queries and mutations against filters.

/**
 * Check if a query matches the given filters
 * Used internally by QueryClient methods for filtering operations
 * @param filters - Query filters to match against
 * @param query - Query instance to test
 * @returns true if query matches all filters
 */
function matchQuery(filters: QueryFilters, query: Query): boolean;

/**
 * Check if a mutation matches the given filters
 * Used internally by QueryClient methods for filtering operations
 * @param filters - Mutation filters to match against
 * @param mutation - Mutation instance to test
 * @returns true if mutation matches all filters
 */
function matchMutation(filters: MutationFilters, mutation: Mutation): boolean;

Usage Examples:

import { matchQuery, matchMutation } from "@tanstack/query-core";

const queryCache = queryClient.getQueryCache();
const mutationCache = queryClient.getMutationCache();

// Custom query filtering
const customFilter = (query: Query) => {
  return matchQuery({
    queryKey: ['user'],
    stale: true,
    active: true,
  }, query);
};

const matchingQueries = queryCache.getAll().filter(customFilter);

// Custom mutation filtering
const pendingUserMutations = mutationCache.getAll().filter(mutation => {
  return matchMutation({
    mutationKey: ['user'],
    status: 'pending',
  }, mutation);
});

// Advanced filtering logic
const complexQueryFilter = (query: Query) => {
  // Match user queries that are either stale or have errors
  const userMatch = matchQuery({ queryKey: ['user'] }, query);
  const staleMatch = matchQuery({ stale: true }, query);
  const errorMatch = matchQuery({ status: 'error' }, query);
  
  return userMatch && (staleMatch || errorMatch);
};

Data Manipulation

Functions for data transformation and structural sharing.

/**
 * Replace data with structural sharing optimization
 * Preserves object references when data hasn't changed
 * @param a - Previous data
 * @param b - New data
 * @returns Data with optimal structural sharing
 */
function replaceEqualDeep<T>(a: unknown, b: T): T;

/**
 * Special function for keeping previous data during updates
 * Used with placeholderData to maintain UI state during refetches
 * @param previousData - The previous data to keep
 * @returns The same data or undefined
 */
function keepPreviousData<T>(previousData: T | undefined): T | undefined;

Usage Examples:

import { replaceEqualDeep, keepPreviousData } from "@tanstack/query-core";

// Structural sharing optimization
const oldUserData = { id: 1, name: 'John', settings: { theme: 'dark' } };
const newUserData = { id: 1, name: 'John', settings: { theme: 'dark' } };

const optimizedData = replaceEqualDeep(oldUserData, newUserData);
// optimizedData.settings === oldUserData.settings (same reference)

// Keep previous data during refetch
const userQuery = new QueryObserver(queryClient, {
  queryKey: ['user', userId],
  queryFn: fetchUser,
  placeholderData: keepPreviousData,
});

// Custom data transformation with structural sharing
const transformData = (oldData, newData) => {
  const transformed = {
    ...newData,
    computed: newData.value * 2,
    timestamp: Date.now(),
  };
  
  return replaceEqualDeep(oldData, transformed);
};

Special Values and Tokens

Special values for conditional behavior and skipping operations.

/**
 * Special token used to skip query execution
 * When used as queryFn, the query will not execute
 */
const skipToken: Symbol;

/**
 * No-operation function
 * Useful as a default or placeholder function
 */
function noop(): void;

/**
 * Check if the current environment is server-side
 * Useful for SSR-aware code
 */
const isServer: boolean;

Usage Examples:

import { skipToken, noop, isServer } from "@tanstack/query-core";

// Conditional query execution
const userQuery = new QueryObserver(queryClient, {
  queryKey: ['user', userId],
  queryFn: userId ? fetchUser : skipToken, // Skip if no userId
});

// Skip query based on conditions
const conditionalQuery = new QueryObserver(queryClient, {
  queryKey: ['data', id],
  queryFn: shouldFetch ? fetchData : skipToken,
});

// No-op function usage
const defaultCallback = noop;

// Server-side detection
if (!isServer) {
  // Client-side only code
  setupBrowserEvents();
}

// SSR-aware query setup
const browserOnlyQuery = new QueryObserver(queryClient, {
  queryKey: ['browser-data'],
  queryFn: isServer ? skipToken : fetchBrowserData,
});

Error Handling

Functions for error handling and decision making.

/**
 * Determine if an error should be thrown based on configuration
 * Used internally to decide whether to throw errors or return them in state
 * @param throwError - Error throwing configuration
 * @param params - Error and query parameters
 * @returns true if error should be thrown
 */
function shouldThrowError<TError, TData>(
  throwError: ThrowOnError<TData, TError> | undefined,
  params: [TError, Query<TData, TError>]
): boolean;

/**
 * Check if a value is a cancelled error
 * Exported from the retryer module
 * @param value - Value to check
 * @returns true if value is a CancelledError
 */
function isCancelledError(value: any): value is CancelledError;

/**
 * Error class for cancelled operations
 * Thrown when queries or mutations are cancelled
 * Exported from the retryer module
 */
class CancelledError extends Error {
  constructor(options?: { revert?: boolean; silent?: boolean });
  revert?: boolean;
  silent?: boolean;
}

Usage Examples:

import { shouldThrowError, CancelledError, isCancelledError } from "@tanstack/query-core";

// Custom error handling logic
const customThrowError = (error, query) => {
  // Only throw errors for specific query types
  if (query.queryKey[0] === 'critical-data') {
    return true;
  }
  
  // Don't throw network errors
  if (error.name === 'NetworkError') {
    return false;
  }
  
  return shouldThrowError(true, [error, query]);
};

// Use in query observer
const observer = new QueryObserver(queryClient, {
  queryKey: ['data'],
  queryFn: fetchData,
  throwOnError: customThrowError,
});

// Handle cancelled errors
try {
  await queryClient.fetchQuery({
    queryKey: ['data'],
    queryFn: fetchData,
  });
} catch (error) {
  if (isCancelledError(error)) {
    console.log('Query was cancelled:', error.message);
    
    if (error.revert) {
      console.log('Should revert optimistic updates');
    }
    
    if (!error.silent) {
      console.log('Should show error message');
    }
  } else {
    console.error('Query failed:', error);
  }
}

// Manual cancellation
const cancelQuery = () => {
  throw new CancelledError({ 
    revert: true, 
    silent: false 
  });
};

Functional Programming Utilities

Type definitions for functional programming patterns and data updates.

/**
 * Type for functional updates
 * Can be either a new value or a function that transforms the old value
 */
type Updater<T> = T | ((old: T) => T);

Usage Examples:

// The Updater type is exported and used throughout the API
type Updater<T> = T | ((old: T) => T);

// Usage in query data updates
queryClient.setQueryData(['user', 123], (oldData) => ({
  ...oldData,
  lastSeen: new Date().toISOString(),
}));

// Custom functional update utility
const updateUserData = (updater: Updater<UserData>) => {
  const currentData = queryClient.getQueryData(['user', userId]);
  const newData = typeof updater === 'function' 
    ? updater(currentData) 
    : updater;
  
  queryClient.setQueryData(['user', userId], newData);
};

// Usage
updateUserData({ name: 'New Name' }); // Direct value
updateUserData(old => ({ ...old, age: old.age + 1 })); // Function update

Development and Debugging

Utilities for development and debugging scenarios.

// Environment detection
if (!isServer) {
  // Enable development tools
  if (process.env.NODE_ENV === 'development') {
    // Development-only code
    window.__QUERY_CLIENT__ = queryClient;
  }
}

// Debug helper for query cache
const debugQueryCache = () => {
  const queries = queryClient.getQueryCache().getAll();
  
  console.table(queries.map(query => ({
    key: JSON.stringify(query.queryKey),
    status: query.state.status,
    fetchStatus: query.state.fetchStatus,
    dataUpdatedAt: new Date(query.state.dataUpdatedAt).toLocaleString(),
    observers: query.observers.length,
    isStale: query.isStale(),
    isActive: query.isActive(),
  })));
};

// Debug helper for mutation cache
const debugMutationCache = () => {
  const mutations = queryClient.getMutationCache().getAll();
  
  console.table(mutations.map(mutation => ({
    id: mutation.mutationId,
    key: JSON.stringify(mutation.options.mutationKey),
    status: mutation.state.status,
    submittedAt: new Date(mutation.state.submittedAt).toLocaleString(),
    variables: JSON.stringify(mutation.state.variables),
  })));
};

// Performance monitoring
const monitorQueryPerformance = () => {
  const cache = queryClient.getQueryCache();
  
  cache.subscribe((event) => {
    if (event.type === 'updated' && event.action.type === 'success') {
      const duration = event.action.dataUpdatedAt - event.action.fetchedAt;
      console.log(`Query ${JSON.stringify(event.query.queryKey)} took ${duration}ms`);
    }
  });
};

Advanced Utility Patterns

Complex utility patterns for advanced use cases.

// Query key factory pattern
const queryKeys = {
  all: ['todos'] as const,
  lists: () => [...queryKeys.all, 'list'] as const,
  list: (filters: string) => [...queryKeys.lists(), { filters }] as const,
  details: () => [...queryKeys.all, 'detail'] as const,
  detail: (id: number) => [...queryKeys.details(), id] as const,
};

// Usage with utilities
const invalidateAllTodos = () => {
  queryClient.invalidateQueries({ queryKey: queryKeys.all });
};

const invalidateListTodos = () => {
  queryClient.invalidateQueries({ queryKey: queryKeys.lists() });
};

// Custom matcher utility
const createQueryMatcher = (baseKey: QueryKey) => {
  return (query: Query): boolean => {
    return partialMatchKey(baseKey, query.queryKey);
  };
};

const todoMatcher = createQueryMatcher(['todos']);
const userMatcher = createQueryMatcher(['user']);

// Find queries using custom matchers
const todoQueries = queryClient.getQueryCache().getAll().filter(todoMatcher);
const userQueries = queryClient.getQueryCache().getAll().filter(userMatcher);

// Batch operations utility
const batchQueryOperations = (operations: Array<() => void>) => {
  notifyManager.batch(() => {
    operations.forEach(op => op());
  });
};

// Usage
batchQueryOperations([
  () => queryClient.setQueryData(['user', 1], userData1),
  () => queryClient.setQueryData(['user', 2], userData2),
  () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
]);

Core Types

type QueryKey = ReadonlyArray<unknown>;
type MutationKey = ReadonlyArray<unknown>;

type Updater<T> = T | ((old: T) => T);

type ThrowOnError<TData, TError, TQueryData = TData, TQueryKey extends QueryKey = QueryKey> = 
  | boolean 
  | ((error: TError, query: Query<TQueryData, TError, TData, TQueryKey>) => boolean);

interface QueryFilters {
  queryKey?: QueryKey;
  exact?: boolean;
  stale?: boolean;
  predicate?: (query: Query) => boolean;
  fetchStatus?: FetchStatus;
  status?: QueryStatus;
  type?: QueryTypeFilter;
}

interface MutationFilters {
  mutationKey?: MutationKey;
  exact?: boolean;
  predicate?: (mutation: Mutation) => boolean;
  status?: MutationStatus;
}

type QueryTypeFilter = 'all' | 'active' | 'inactive';
type FetchStatus = 'fetching' | 'paused' | 'idle';
type QueryStatus = 'pending' | 'error' | 'success';
type MutationStatus = 'idle' | 'pending' | 'success' | 'error';

Install with Tessl CLI

npx tessl i tessl/npm-tanstack--query-core

docs

browser-integration.md

cache-management.md

client-management.md

hydration.md

index.md

infinite-queries.md

mutations.md

query-observers.md

query-operations.md

utilities.md

tile.json