Query Keys
Purpose and Scope
Query keys are the cache identity contract in TanStack Query. A query key names the server-state resource being read and includes the inputs that make one cached result different from another. When components, prefetching code, invalidation code, devtools, and tests all use the same key shape, they can talk about the same cache entry without sharing component state. The official Query positioning describes keys as the language used by reads, writes, invalidation, prefetching, and devtools; this page turns that concept into practical design rules for application code.
TanStack Query is intentionally backend-agnostic: query functions can call REST endpoints, GraphQL clients, Angular HttpClient, browser fetch, or any promise-returning data source. That flexibility makes key design more important, not less important, because the cache cannot infer identity from the transport layer. The Angular HttpClient guide shows a query whose key is ['repoData'] while the query function converts an HttpClient observable into a promise. The key is the stable cache address; the query function is only the mechanism that fills it. Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
Relevant Source Files
docs/framework/angular/reference/functions/provideTanStackQuery.md- Shows how Angular applications provide a sharedQueryClient, which is the object that owns query caches keyed by query keys.docs/community-resources.md- Lists community utilities including Query Key Factory, which reflects the project’s recommendation to standardize query keys for cache management at application scale.docs/config.json- Defines the documentation structure across frameworks, showing that query concepts are shared while examples and APIs are surfaced through framework-specific docs.docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md- Provides an AngularinjectQueryexample with a concretequeryKeyand a promise-basedqueryFn.docs/framework/angular/devtools.md- Documents devtools setup for Angular, useful because devtools expose query and mutation cache state for debugging key design.docs/framework/angular/guides/background-fetching-indicators.md- ShowsinjectQuerywithqueryKey: ['todos']and status flags that demonstrate how one cache entry can be observed while it fetches in the background.
Core Model: Keys Identify Cache Entries
A query key is normally an array. The first segment should identify the broad resource or domain, and later segments should describe scope, route parameters, filters, pagination state, locale, tenant, or any other input that changes the fetched data. For example, ['todos'] represents the general todos collection, while ['todos', { status: 'open' }] and ['todos', { status: 'done' }] should be separate cache entries because they represent different server results. Treat the key as a declarative description of data, not as a label for a component.
The Angular background-fetching guide uses queryKey: ['todos'] in a component that renders pending, error, success, and refreshing UI states. That example is small, but it illustrates the main rule: the key is independent of those UI states. The same cache entry can be pending, successful, stale, refetching, or errored over time, but it remains the same entry while its key remains the same. Components subscribe to that key and TanStack Query coordinates the fetch lifecycle for all observers. Sources: docs/framework/angular/guides/background-fetching-indicators.md
Keys must include every variable that affects the query function’s returned data. If a query function calls /api/todos?status=open, the key should include status: 'open'. If it reads a user id from a route, the id belongs in the key. If it depends on an injected service configuration that changes per tenant, tenant identity belongs in the key. Leaving an input out can cause unrelated views to share stale or incorrect data. Adding irrelevant values has the opposite problem: it fragments the cache and prevents useful reuse.
Deterministic Structure and Matching
TanStack Query hashes query keys deterministically so equivalent serializable key structures identify the same cache entry. In practice, design keys from JSON-compatible values such as strings, numbers, booleans, arrays, and plain objects. Object segments are useful for filters because they preserve names for optional parameters, while array segment order remains meaningful for hierarchy. A common pattern is ['projects', projectId, 'issues', { status, sort }], where each segment narrows the resource from broad domain to exact view.
Matching is what lets one key design serve more than one task. A component reads one exact key; invalidation or refetching can target a broader key prefix such as ['todos']; devtools can group and inspect entries by visible key shape. The official QueryClient reference lists methods such as invalidateQueries, refetchQueries, cancelQueries, removeQueries, resetQueries, getQueryData, setQueryData, and isFetching, all of which become more predictable when the application uses consistent key prefixes and exact keys.
Standardizing keys becomes especially valuable in larger codebases. The community resources page includes Query Key Factory, described as a library for creating typesafe standardized query keys for cache management in @tanstack/query. You do not need that utility to use TanStack Query well, but its presence in the ecosystem highlights the same design pressure: cache operations are easier when keys are generated from one shared vocabulary instead of recreated as string arrays across many components. Sources: docs/community-resources.md
Framework Mapping: Angular Examples
The Angular adapter exposes TanStack Query through Angular-native provider and injection APIs, but the query key concept stays the same as in React, Vue, Solid, Svelte, Preact, and Lit. The provider reference shows provideTanStackQuery(new QueryClient()), which installs a shared client for the application or for a lazy-loaded boundary. That shared QueryClient is where keyed query state is coordinated, so accidentally creating separate clients is also accidentally creating separate key spaces. Sources: docs/framework/angular/reference/functions/provideTanStackQuery.md
import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental'
bootstrapApplication(AppComponent, {
providers: [provideTanStackQuery(new QueryClient())],
})Once a client is provided, Angular components can declare keyed queries with injectQuery. The HttpClient guide demonstrates a repoData query whose fetcher uses lastValueFrom around this.http.get(...). That is an important separation of concerns: Angular HttpClient handles interceptors, tests, pending tasks, and SSR request caching, while TanStack Query still uses the key to manage its own cache lifecycle. If the GitHub repository owner or name became dynamic, those values should move into the key beside repoData. Sources: docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md
readonly query = injectQuery(() => ({
queryKey: ['repoData'],
queryFn: () =>
lastValueFrom(
this.http.get('https://api.github.com/repos/tanstack/query'),
),
}))Observability, Devtools, and Background Fetching
Good keys make invisible cache behavior visible. Angular Query Devtools are enabled by passing withDevtools() to provideTanStackQuery, and the Angular devtools guide explains that the tools help debug and inspect queries and mutations. When a list, detail page, and global fetching indicator all appear to disagree, the first debugging step is to inspect whether they are actually using the same key, related prefix keys, or accidentally unrelated structures. Sources: docs/framework/angular/devtools.md
Background fetching indicators also depend on shared cache observation. The Angular guide shows a component-level todosQuery.isFetching() indicator and a global injectIsFetching() indicator. The local indicator belongs to the ['todos'] query, while the global indicator counts fetching activity across the client. This distinction is useful when designing keys: use exact keys for component data, use broad filters for global or section-level activity, and avoid embedding ephemeral UI state that would create unnecessary cache entries. Sources: docs/framework/angular/guides/background-fetching-indicators.md
The docs configuration file shows TanStack Query documentation organized by framework while preserving common conceptual areas. That structure mirrors the implementation model readers should keep in mind: framework adapters provide native ergonomics, but server-state concepts such as keys, cache entries, query clients, devtools, and background fetching are shared. If you learn the key vocabulary in Angular, the same key-design principles transfer to React hooks, Vue composables, Solid functions, and other adapters even though the surrounding API names differ. Sources: docs/config.json
Compact Reference
| Concern | Recommendation | Example |
|---|---|---|
| Top-level resource | Start with a stable domain noun | ['todos'] |
| Entity identity | Put ids in later ordered segments | ['todos', todoId] |
| Filters and options | Use a plain object segment for named parameters | ['todos', { status, sort }] |
| Pagination | Include page, cursor, or search parameters that change returned data | ['issues', projectId, { page }] |
| Invalidation | Choose prefixes that map to real refetch boundaries | invalidate ['todos'] to refresh todo-related queries |
| Debugging | Keep keys readable in devtools | prefer domain words over opaque generated strings |
Use this checklist when adding a new query. First, name the resource from the user’s point of view. Second, list every variable the query function reads to decide what data to return. Third, decide which prefix you will want to invalidate after mutations. Fourth, verify that the same key factory or helper is used by readers, prefetchers, invalidations, and cache updates. Finally, inspect the result with devtools during development to confirm that the key shape remains stable across renders.
Next Steps
After designing query keys, read the QueryClient reference to understand the operations that consume them, especially getQueryData, setQueryData, invalidateQueries, refetchQueries, and isFetching. Then pair this page with the cache lifecycle, filters and matching, invalidations from mutations, and framework-adapter pages. For Angular applications specifically, confirm that provideTanStackQuery is installed once at the intended application boundary, use injectQuery with complete keys, and enable devtools while you refine cache identity.