Signals for managing, caching and syncing asynchronous and remote data in Angular
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
TanStack Angular Query (Experimental) is an Angular adapter for TanStack Query that enables reactive data fetching, caching, and state management using Angular's signals system. It provides transport-agnostic data fetching capabilities supporting REST, GraphQL, and any promise-based data sources, with automatic caching and refetching strategies.
npm install @tanstack/angular-query-experimentalimport {
provideTanStackQuery,
QueryClient,
injectQuery,
injectMutation,
injectInfiniteQuery
} from "@tanstack/angular-query-experimental";For CommonJS:
const {
provideTanStackQuery,
QueryClient,
injectQuery,
injectMutation
} = require("@tanstack/angular-query-experimental");// 1. Bootstrap with TanStack Query
import { provideTanStackQuery, QueryClient } from "@tanstack/angular-query-experimental";
bootstrapApplication(AppComponent, {
providers: [provideTanStackQuery(new QueryClient())],
});
// 2. Use in components/services
import { injectQuery, injectMutation } from "@tanstack/angular-query-experimental";
import { Component, inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
@Component({
selector: 'app-todos',
template: `
<div *ngIf="todosQuery.isPending()">Loading...</div>
<div *ngIf="todosQuery.isError()">Error: {{ todosQuery.error()?.message }}</div>
<div *ngIf="todosQuery.isSuccess()">
<ul>
<li *ngFor="let todo of todosQuery.data()">{{ todo.title }}</li>
</ul>
</div>
`
})
export class TodosComponent {
#http = inject(HttpClient);
// Query with signals - automatically reactive
todosQuery = injectQuery(() => ({
queryKey: ['todos'],
queryFn: () => this.#http.get<Todo[]>('/api/todos')
}));
// Mutation for creating todos
createTodoMutation = injectMutation(() => ({
mutationFn: (todo: CreateTodo) => this.#http.post<Todo>('/api/todos', todo),
onSuccess: () => {
// Invalidate and refetch todos
inject(QueryClient).invalidateQueries({ queryKey: ['todos'] });
}
}));
}
interface Todo {
id: number;
title: string;
completed: boolean;
}
interface CreateTodo {
title: string;
}TanStack Angular Query is built around several key components:
injectQuery, injectMutation, etc.)Core query functionality for fetching and caching data with automatic refetching, stale-while-revalidate patterns, and reactive updates.
function injectQuery<TQueryFnData, TError, TData, TQueryKey>(
injectQueryFn: () => CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
options?: InjectQueryOptions
): CreateQueryResult<TData, TError>;
interface CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey> {
queryKey: TQueryKey;
queryFn: QueryFunction<TQueryFnData, TQueryKey>;
enabled?: boolean;
staleTime?: number;
refetchOnWindowFocus?: boolean;
// ... other query options
}
interface CreateQueryResult<TData, TError> {
data: Signal<TData | undefined>;
error: Signal<TError | null>;
isLoading: Signal<boolean>;
isPending: Signal<boolean>;
isSuccess: Signal<boolean>;
isError: Signal<boolean>;
// ... other result properties
}Mutation functionality for server-side effects with optimistic updates, error handling, and automatic query invalidation.
function injectMutation<TData, TError, TVariables, TContext>(
injectMutationFn: () => CreateMutationOptions<TData, TError, TVariables, TContext>,
options?: InjectMutationOptions
): CreateMutationResult<TData, TError, TVariables, TContext>;
interface CreateMutationOptions<TData, TError, TVariables, TContext> {
mutationFn: MutationFunction<TData, TVariables>;
onSuccess?: (data: TData, variables: TVariables, context: TContext) => void;
onError?: (error: TError, variables: TVariables, context: TContext) => void;
// ... other mutation options
}
interface CreateMutationResult<TData, TError, TVariables, TContext> {
mutate: CreateMutateFunction<TData, TError, TVariables, TContext>;
mutateAsync: CreateMutateAsyncFunction<TData, TError, TVariables, TContext>;
data: Signal<TData | undefined>;
error: Signal<TError | null>;
isSuccess: Signal<boolean>;
isError: Signal<boolean>;
isPending: Signal<boolean>;
// ... other result properties
}Infinite query functionality for pagination and infinite scrolling with automatic data accumulation and page management.
function injectInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
injectInfiniteQueryFn: () => CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
options?: InjectInfiniteQueryOptions
): CreateInfiniteQueryResult<TData, TError>;
interface CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> {
queryKey: TQueryKey;
queryFn: InfiniteQueryFunction<TQueryFnData, TQueryKey, TPageParam>;
initialPageParam: TPageParam;
getNextPageParam: (lastPage: TQueryFnData, allPages: TQueryFnData[], lastPageParam: TPageParam) => TPageParam | null;
getPreviousPageParam?: (firstPage: TQueryFnData, allPages: TQueryFnData[], firstPageParam: TPageParam) => TPageParam | null;
// ... other infinite query options
}Provider functions for setting up TanStack Query in Angular applications with optional features like developer tools.
function provideTanStackQuery(
queryClient: QueryClient | InjectionToken<QueryClient>,
...features: Array<QueryFeatures>
): Array<Provider>;
function withDevtools(
withDevtoolsFn?: () => DevtoolsOptions
): DeveloperToolsFeature;
interface DevtoolsOptions {
initialIsOpen?: boolean;
buttonPosition?: DevtoolsButtonPosition;
position?: DevtoolsPosition;
loadDevtools?: 'auto' | boolean;
// ... other devtools options
}Functions for monitoring query and mutation states across the application for loading indicators and debugging.
function injectIsFetching(
filters?: QueryFilters,
options?: InjectIsFetchingOptions
): Signal<number>;
function injectIsMutating(
filters?: MutationFilters,
options?: InjectIsMutatingOptions
): Signal<number>;
function injectIsRestoring(
options?: InjectIsRestoringOptions
): Signal<boolean>;Functions for handling multiple queries simultaneously with type-safe results and combined operations.
function injectQueries<T extends Array<any>, TCombinedResult = QueriesResults<T>>(
config: {
queries: Signal<[...QueriesOptions<T>]>;
combine?: (result: QueriesResults<T>) => TCombinedResult;
},
injector?: Injector
): Signal<TCombinedResult>;
function injectMutationState<TResult = MutationState>(
injectMutationStateFn?: () => MutationStateOptions<TResult>,
options?: InjectMutationStateOptions
): Signal<Array<TResult>>;Type-safe utility functions for creating reusable query and mutation options with proper type inference.
function queryOptions<TQueryFnData, TError, TData, TQueryKey>(
options: CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>
): CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
queryKey: DataTag<TQueryKey, TQueryFnData, TError>;
};
function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
options: CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
): CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>;
};
function mutationOptions<TData, TError, TVariables, TContext>(
options: CreateMutationOptions<TData, TError, TVariables, TContext>
): CreateMutationOptions<TData, TError, TVariables, TContext>;// Re-exported from @tanstack/query-core
class QueryClient {
constructor(config?: QueryClientConfig);
getQueryData<TData>(queryKey: QueryKey): TData | undefined;
setQueryData<TData>(queryKey: QueryKey, data: TData): TData;
invalidateQueries(filters?: QueryFilters): Promise<void>;
// ... other QueryClient methods
}
// Angular-specific injection options
interface InjectQueryOptions {
injector?: Injector;
}
interface InjectMutationOptions {
injector?: Injector;
}
interface InjectInfiniteQueryOptions {
injector?: Injector;
}
// Common type aliases
type QueryKey = ReadonlyArray<unknown>;
type DefaultError = Error;