Skip to main content

Query Key Management

Query keys are used to identify and manage cached queries. Builder automatically generates hierarchical query keys based on your endpoint URLs and parameters.

Key Structure​

Query keys follow a hierarchical structure:

[...keyPrefix, ...urlParts, ...keySuffix, queryParams]

Example​

const getUser = client.query({
method: 'GET',
url: '/users/$userId',
responseSchema: userSchema,
processResponse: (data) => data,
})

// URL: /users/123?include=posts
// keyPrefix: ['api']
// Key: ['api', 'users', '123', { include: 'posts' }]

Key Methods​

dataTag​

Get the full query key with all parameters:

const getUser = client.query({
method: 'GET',
url: '/users/$userId',
querySchema: z.object({
include: z.string().optional(),
}),
responseSchema: userSchema,
processResponse: (data) => data,
})

// Full key with all parameters
const key = getUser.queryKey.dataTag({
urlParams: { userId: '123' },
params: { include: 'posts' },
})
// => ['users', '123', { include: 'posts' }]

filterKey​

Get a partial key for filtering (URL params only, ignores query params):

// Partial key for filtering
const filterKey = getUser.queryKey.filterKey({
urlParams: { userId: '123' },
})
// => ['users', '123']

bindToUrl​

Get the resolved URL string:

const url = getUser.queryKey.bindToUrl({
urlParams: { userId: '123' },
})
// => '/users/123'

Using Query Keys​

Manual Cache Access​

import { useQueryClient } from '@tanstack/react-query'

function UserProfile({ userId }: { userId: string }) {
const queryClient = useQueryClient()

// Get cached data
const cachedUser = queryClient.getQueryData(
getUser.queryKey.dataTag({ urlParams: { userId } })
)

// Set cached data
queryClient.setQueryData(
getUser.queryKey.dataTag({ urlParams: { userId } }),
{ id: userId, name: 'John', email: '[email protected]' }
)

return <div>{/* ... */}</div>
}

Invalidation​

import { useQueryClient } from '@tanstack/react-query'

function UserProfile({ userId }: { userId: string }) {
const queryClient = useQueryClient()

const handleRefresh = () => {
// Invalidate specific query
queryClient.invalidateQueries({
queryKey: getUser.queryKey.dataTag({ urlParams: { userId } }),
})
}

return <button onClick={handleRefresh}>Refresh</button>
}

Filtering Queries​

import { useQueryClient } from '@tanstack/react-query'

function UserList() {
const queryClient = useQueryClient()

const handleRefreshAll = () => {
// Invalidate all user queries (ignores query params)
queryClient.invalidateQueries({
queryKey: getUser.queryKey.filterKey({ urlParams: { userId: 'any' } }),
})
}

return <button onClick={handleRefreshAll}>Refresh All Users</button>
}

Key Prefix and Suffix​

Configure default key prefix/suffix when creating the client:

const client = declareClient({
api,
defaults: {
keyPrefix: ['api', 'v1'], // All keys will start with ['api', 'v1']
keySuffix: ['cache'], // All keys will end with ['cache']
},
})

const getUser = client.query({
method: 'GET',
url: '/users/$userId',
responseSchema: userSchema,
processResponse: (data) => data,
})

// Key: ['api', 'v1', 'users', '123', 'cache']
const key = getUser.queryKey.dataTag({ urlParams: { userId: '123' } })

Common Patterns​

function UserProfile({ userId }: { userId: string }) {
const queryClient = useQueryClient()

const handleUpdate = async () => {
// After updating user, invalidate related queries
await queryClient.invalidateQueries({
queryKey: getUser.queryKey.filterKey({ urlParams: { userId } }),
})
await queryClient.invalidateQueries({
queryKey: getUserPosts.queryKey.filterKey({ urlParams: { userId } }),
})
}

return <button onClick={handleUpdate}>Update</button>
}

Prefetch Data​

function UserList() {
const queryClient = useQueryClient()

const handlePrefetch = (userId: string) => {
queryClient.prefetchQuery({
queryKey: getUser.queryKey.dataTag({ urlParams: { userId } }),
queryFn: () => getUser({ urlParams: { userId } }),
})
}

return (
<div>
{users.map((user) => (
<div
key={user.id}
onMouseEnter={() => handlePrefetch(user.id)}
>
{user.name}
</div>
))}
</div>
)
}

Manual Cache Updates​

function UserProfile({ userId }: { userId: string }) {
const queryClient = useQueryClient()

const handleOptimisticUpdate = (newName: string) => {
// Update cache optimistically
queryClient.setQueryData(
getUser.queryKey.dataTag({ urlParams: { userId } }),
(old: User | undefined) => {
if (!old) return old
return { ...old, name: newName }
}
)
}

return <div>{/* ... */}</div>
}

Next Steps​