Eden TanStack Query
Guides

Query Keys

Understanding query key structure and cache operations

Query Keys

Eden TanStack Query generates deterministic query keys from your route paths and input. These keys are used by TanStack Query for caching, deduplication, and invalidation.

Key Structure

Query keys follow this structure:

// queryKey() returns:
[['path', 'segments', 'method'], { input: { ... }, type: 'query' }]

// Examples:
eden.users.get.queryKey()
// => [['users', 'get'], { type: 'query' }]

eden.users.get.queryKey({ role: 'admin' })
// => [['users', 'get'], { input: { role: 'admin' }, type: 'query' }]

eden.users({ id: '1' }).get.queryKey()
// => [['users', 'get'], {
//   pathParams: [{ pathIndex: 0, entries: [['id', '1']] }],
//   type: 'query'
// }]

The type field differentiates regular queries from infinite queries:

eden.posts.get.infiniteQueryKey({ limit: 10 })
// => [['posts', 'get'], {
//   input: { limit: 10 },
//   type: 'infinite',
//   infinite: { initialPageParam: null }
// }]

Cache Identity

Path parameters

Path parameters are stored separately from request input and retain their path position and application order. A query field cannot overwrite path identity, and sequential parameters with the same name remain distinct.

Request headers

Per-request headers are included in the input part of the query key. Different tenant, authorization, locale, or other response-varying headers therefore create separate cache entries.

Header values are visible in TanStack Query Devtools and can be serialized by dehydration or persistence. Treat raw secrets in per-request headers as an explicit cache-identity trade-off, and only use them when that exposure is understood and controlled.

skipToken

skipToken is supported by queryOptions() and infiniteQueryOptions() to create disabled queries. It is not valid input for key-only helpers: queryKey(skipToken) and infiniteQueryKey(skipToken) throw an error.

Available Methods

queryKey()

Returns the query key for a specific route. Optionally accepts input for more specific targeting:

eden.users.get.queryKey()                              // all users queries
eden.users.get.queryKey({ role: 'admin' })  // specific input
eden.users({ id: '1' }).get.queryKey()                 // specific path params

queryFilter()

Returns a filter object (including queryKey) for use with TanStack Query's filter-based methods:

eden.users.get.queryFilter()
eden.users.get.queryFilter({ role: 'admin' })
eden.users.get.queryFilter(undefined, { exact: true, stale: true })

queryKey() generates keys with type: "query", which only matches regular queries. queryFilter() uses type: "any", which matches both regular and infinite queries — making it better suited for broad cache invalidation.

infiniteQueryKey()

Returns the query key for infinite queries:

eden.posts.get.infiniteQueryKey()
eden.posts.get.infiniteQueryKey({ limit: 10 })
eden.posts.get.infiniteQueryKey({ limit: 10 }, { initialCursor: 'start' })

The initial cursor is part of an exact infinite-query key. The helper defaults it to null for optional cursor routes, matching infiniteQueryOptions(). Required cursor routes must pass the same explicit non-null initialCursor to both helpers.

infiniteQueryFilter()

Returns a filter object for infinite queries:

eden.posts.get.infiniteQueryFilter()
eden.posts.get.infiniteQueryFilter({ category: 'tech' })
eden.posts.get.infiniteQueryFilter(
  { category: 'tech' },
  { initialCursor: 'start' }
)

Infinite-query filters match all initial-cursor variants by default. Pass initialCursor in the filter options to target one variant. With the default hash, cursor values are compared as complete values rather than partial object or array prefixes, while route input keeps TanStack Query's normal partial matching. A custom queryKeyHashFn defines cursor identity just as it does for cache entries. On optional cursor routes, exact: true uses the default null cursor when initialCursor is omitted. Required cursor routes allow an omitted cursor only for broad filters with exact absent or false; exact and cursor-specific filters require an explicit non-null cursor.

mutationKey()

Returns the mutation key for a mutation route:

eden.users.post.mutationKey()
// => [['users', 'post']]

Cache Operations

invalidateQueries

Mark queries as stale and trigger a refetch:

const queryClient = useQueryClient()
const eden = useEden()

// Invalidate all users queries (partial matching)
queryClient.invalidateQueries({
  queryKey: eden.users.get.queryKey()
})

// Invalidate with queryFilter
queryClient.invalidateQueries(
  eden.users.get.queryFilter()
)

// Invalidate a specific user
queryClient.invalidateQueries({
  queryKey: eden.users({ id: '1' }).get.queryKey()
})

cancelQueries

Cancel in-flight queries (useful for optimistic updates):

await queryClient.cancelQueries({
  queryKey: eden.todos.get.queryKey()
})

getQueryData / setQueryData

Read and write the cache directly:

// Read cached data
const user = queryClient.getQueryData(
  eden.users({ id: '1' }).get.queryKey()
)

// Write to cache
queryClient.setQueryData(
  eden.users({ id: '1' }).get.queryKey(),
  { id: '1', name: 'Updated Name', email: 'updated@example.com' }
)

removeQueries

Remove queries from the cache entirely:

queryClient.removeQueries({
  queryKey: eden.users({ id: '1' }).get.queryKey()
})

Partial Matching

TanStack Query uses partial matching for query keys. A key without input matches all queries for that route:

// Invalidates ALL users queries, regardless of input
queryClient.invalidateQueries({
  queryKey: eden.users.get.queryKey()
})

// Only invalidates the specific query with role='admin'
queryClient.invalidateQueries({
  queryKey: eden.users.get.queryKey({ role: 'admin' })
})

This is useful for broad invalidation after mutations:

const createUser = useMutation({
  ...eden.users.post.mutationOptions(),
  onSuccess: () => {
    // Invalidates all users queries (list, filtered, by id, etc.)
    queryClient.invalidateQueries({
      queryKey: eden.users.get.queryKey()
    })
  },
})

For more details on how partial matching works, see the TanStack Query docs on query invalidation.

On this page