Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6dd8fbf
init query rename and delegation
DogPawHat Nov 1, 2025
c824b11
update spelling
DogPawHat Nov 1, 2025
4f5f27a
add respect for select
DogPawHat Nov 2, 2025
c7bea26
react query options testing
DogPawHat Nov 2, 2025
269ed8e
update changeset
DogPawHat Nov 2, 2025
f0217b4
ci: apply automated fixes
autofix-ci[bot] Nov 2, 2025
19b13cf
fixes
DogPawHat Nov 2, 2025
311b3ba
more type fixes
DogPawHat Nov 2, 2025
fd8ea67
fix typo
DogPawHat Nov 2, 2025
058fd5c
typo again
DogPawHat Nov 3, 2025
a2c989b
Type fix
DogPawHat Nov 3, 2025
a78d0fe
revert delegations
DogPawHat Nov 5, 2025
de3f12d
typo
DogPawHat Nov 5, 2025
1ed5851
client update async
DogPawHat Nov 5, 2025
5c40184
REVERT IF OPUS FUCKED UP
DogPawHat Nov 27, 2025
bf15113
pages nit
DogPawHat Nov 27, 2025
88d795b
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Dec 27, 2025
a905c54
ci: apply automated fixes
autofix-ci[bot] Dec 27, 2025
f89cc80
use a stub query options function
DogPawHat Dec 27, 2025
fedf7b5
use query and infiniteQuery functions directly for type inference
DogPawHat Dec 28, 2025
a743447
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Dec 28, 2025
c7baef7
lint array fix
DogPawHat Dec 28, 2025
0c6ea77
remove explicit typing
DogPawHat Dec 28, 2025
3b0207d
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Dec 28, 2025
984739c
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Feb 14, 2026
08882ab
throw error if enabled: true/skiptoken and no cached data
DogPawHat Feb 23, 2026
c141fe9
fix title
DogPawHat Feb 23, 2026
8c00297
fix title
DogPawHat Feb 24, 2026
09c7acd
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Feb 24, 2026
3e6d373
correct the changeset
DogPawHat Feb 25, 2026
d9f05cc
add more tests for better coverage
DogPawHat Feb 25, 2026
ce4dcb6
better names in tests
DogPawHat Feb 25, 2026
6c25c3c
more test coverage
DogPawHat Feb 25, 2026
4d70a42
typo
DogPawHat Feb 25, 2026
21be54a
check if error throw in query is redudant
DogPawHat Feb 26, 2026
c44375a
ci: apply automated fixes
autofix-ci[bot] Feb 26, 2026
bbdad0a
remove accidental md commit
DogPawHat Feb 27, 2026
f6f4df2
change error message
DogPawHat Feb 27, 2026
43b553a
update incorrect enabled
DogPawHat Feb 27, 2026
cf7b252
update tests
DogPawHat Feb 27, 2026
6ccd459
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Mar 4, 2026
fff81d9
reduce size of diff slighly
DogPawHat Mar 4, 2026
64547d4
Merge branch 'main' into implement-simplified-imperitive-methods
DogPawHat Apr 3, 2026
6862fec
inital docs pass
DogPawHat Apr 4, 2026
d8c3b75
ci: apply automated fixes
autofix-ci[bot] Apr 4, 2026
c9f7020
deprecation notices for methods
DogPawHat Apr 4, 2026
f9fd762
extra tests for angular
DogPawHat Apr 4, 2026
8e7a2a7
query options test for packages not yet tested
DogPawHat Apr 4, 2026
f47d9ba
react query tests update
DogPawHat Apr 4, 2026
246afbd
update change set
DogPawHat Apr 4, 2026
a6289bc
more tests
DogPawHat Apr 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/famous-owls-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@tanstack/query-core': minor
'@tanstack/vue-query': minor
---

add query() and infiniteQuery() imperative methods to QueryClient
2 changes: 1 addition & 1 deletion docs/eslint/stable-query-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function App() {
```tsx
async function App() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(options)
await queryClient.query(options)
}
```

Expand Down
10 changes: 6 additions & 4 deletions docs/framework/angular/guides/paginated-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,12 @@ export class PaginationExampleComponent {
effect(() => {
// Prefetch the next page!
if (!this.query.isPlaceholderData() && this.query.data()?.hasMore) {
this.#queryClient.prefetchQuery({
queryKey: ['projects', this.page() + 1],
queryFn: () => lastValueFrom(fetchProjects(this.page() + 1)),
})
void this.#queryClient
.query({
queryKey: ['projects', this.page() + 1],
queryFn: () => lastValueFrom(fetchProjects(this.page() + 1)),
})
.catch(noop)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/angular/guides/query-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ queries = inject(QueriesService)

postQuery = injectQuery(() => this.queries.post(this.postId()))

queryClient.prefetchQuery(this.queries.post(23))
queryClient.query(this.queries.post(23)).catch(noop)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether noop is defined in this markdown file snippet
rg -n 'noop|queryClient\.query' docs/framework/angular/guides/query-options.md -C2

Repository: TanStack/query

Length of output: 262


🏁 Script executed:

# Check broader context around line 41 and search for noop definition in the entire file
cat -n docs/framework/angular/guides/query-options.md | sed -n '1,80p'

Repository: TanStack/query

Length of output: 1628


🏁 Script executed:

# Search entire markdown file for 'noop' definition or import
rg -n 'noop' docs/framework/angular/guides/query-options.md

Repository: TanStack/query

Length of output: 113


🏁 Script executed:

# Check if noop is imported or used elsewhere in the codebase
rg -n "import.*noop|from.*noop|noop.*=" docs/framework/angular/guides/ -A1 -B1

Repository: TanStack/query

Length of output: 40


🏁 Script executed:

# Check if noop is defined/imported in test files or other parts of the codebase
rg -n "noop" packages/solid-query-persist-client/src/__tests__/ -B2 -A1 | head -30

Repository: TanStack/query

Length of output: 474


noop is undefined in this code sample.

The code snippet shows a usage example but does not define or import noop, making the example non-functional. Replace with an inline error handler:

Suggested fix
-queryClient.query(this.queries.post(23)).catch(noop)
+queryClient.query(this.queries.post(23)).catch(() => undefined)
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
queryClient.query(this.queries.post(23)).catch(noop)
queryClient.query(this.queries.post(23)).catch(() => undefined)
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/angular/guides/query-options.md` at line 41, The example calls
queryClient.query(this.queries.post(23)).catch(noop) but noop is not defined;
replace the undefined noop with an inline no-op error handler (e.g., a lambda
that accepts the error and does nothing) so the snippet is self-containedβ€”update
the call to use .catch(err => { /* no-op */ }) or .catch(() => {}) referencing
queryClient.query and this.queries.post to locate the line.

queryClient.setQueryData(this.queries.post(42).queryKey, newPost)
```

Expand Down
6 changes: 4 additions & 2 deletions docs/framework/angular/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ computed(() => {

## Typing Query Options

If you inline query options into `injectQuery`, you'll get automatic type inference. However, you might want to extract the query options into a separate function to share them between `injectQuery` and e.g. `prefetchQuery` or manage them in a service. In that case, you'd lose type inference. To get it back, you can use the `queryOptions` helper:
If you inline query options into `injectQuery`, you'll get automatic type inference. However, you might want to extract the query options into a separate function to share them between `injectQuery` and imperative calls like `queryClient.query`, or manage them in a service. In that case, you'd lose type inference. To get it back, you can use the `queryOptions` helper:

```ts
@Injectable({
Expand Down Expand Up @@ -215,11 +215,13 @@ export class Component {
postQuery = injectQuery(this.optionsSignal)

someMethod() {
this.queryClient.prefetchQuery(this.queries.post(23))
this.queryClient.query(this.queries.post(23)).catch(noop)
}
}
```

Because `queryClient.query` preserves `select` and `enabled`, the extracted options behave the same way in both places. The legacy `fetchQuery` and `prefetchQuery` APIs still accept those options at the type level, but they ignore `select` and `enabled` at runtime.

Comment on lines +223 to +224
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Runtime behavior note is now inaccurate for fetchQuery/prefetchQuery.

Line 223 says legacy APIs ignore select/enabled at runtime, but in this PR they delegate to query, so this statement is misleading.

Suggested docs correction
-Because `queryClient.query` preserves `select` and `enabled`, the extracted options behave the same way in both places. The legacy `fetchQuery` and `prefetchQuery` APIs still accept those options at the type level, but they ignore `select` and `enabled` at runtime.
+Because `queryClient.query` preserves `select` and `enabled`, the extracted options behave the same way in both places. `fetchQuery` and `prefetchQuery` are legacy aliases and follow the same runtime behavior.
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Because `queryClient.query` preserves `select` and `enabled`, the extracted options behave the same way in both places. The legacy `fetchQuery` and `prefetchQuery` APIs still accept those options at the type level, but they ignore `select` and `enabled` at runtime.
Because `queryClient.query` preserves `select` and `enabled`, the extracted options behave the same way in both places. `fetchQuery` and `prefetchQuery` are legacy aliases and follow the same runtime behavior.
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/angular/typescript.md` around lines 223 - 224, Update the
runtime behavior note to reflect that legacy APIs now delegate to query: change
the sentence that claims fetchQuery and prefetchQuery "ignore `select` and
`enabled` at runtime" to state that fetchQuery and prefetchQuery delegate to
queryClient.query (via query) and therefore honor `select` and `enabled` at
runtime; reference the symbols fetchQuery, prefetchQuery, and queryClient.query
(or query) so readers know which implementation now enforces those options.

Further, the `queryKey` returned from `queryOptions` knows about the `queryFn` associated with it, and we can leverage that type information to make functions like `queryClient.getQueryData` aware of those types as well:

```ts
Expand Down
68 changes: 40 additions & 28 deletions docs/framework/react/guides/advanced-ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ import {
export async function getStaticProps() {
const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: getPosts,
})
await queryClient
.query({
queryKey: ['posts'],
queryFn: getPosts,
})
.catch(noop)

return {
props: {
Expand Down Expand Up @@ -172,10 +174,12 @@ import Posts from './posts'
export default async function PostsPage() {
const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: getPosts,
})
await queryClient
.query({
queryKey: ['posts'],
queryFn: getPosts,
})
.catch(noop)

return (
// Neat! Serialization is now as easy as passing props.
Expand Down Expand Up @@ -237,10 +241,12 @@ import CommentsServerComponent from './comments-server'
export default async function PostsPage() {
const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: getPosts,
})
await queryClient
.query({
queryKey: ['posts'],
queryFn: getPosts,
})
.catch(noop)

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand All @@ -261,10 +267,12 @@ import Comments from './comments'
export default async function CommentsServerComponent() {
const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['posts-comments'],
queryFn: getComments,
})
await queryClient
.query({
queryKey: ['posts-comments'],
queryFn: getComments,
})
.catch(noop)

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand Down Expand Up @@ -325,8 +333,8 @@ import Posts from './posts'
export default async function PostsPage() {
const queryClient = new QueryClient()

// Note we are now using fetchQuery()
const posts = await queryClient.fetchQuery({
// Note we are getting the result from query
const posts = await queryClient.query({
queryKey: ['posts'],
queryFn: getPosts,
})
Expand Down Expand Up @@ -355,7 +363,7 @@ Using React Query with Server Components makes most sense if:

It's hard to give general advice on when it makes sense to pair React Query with Server Components and not. **If you are just starting out with a new Server Components app, we suggest you start out with any tools for data fetching your framework provides you with and avoid bringing in React Query until you actually need it.** This might be never, and that's fine, use the right tool for the job!

If you do use it, a good rule of thumb is to avoid `queryClient.fetchQuery` unless you need to catch errors. If you do use it, don't render its result on the server or pass the result to another component, even a Client Component one.
If you do use it, a good rule of thumb is to avoid rendering the result of `queryClient.query` on the server or passing it to another component, even a Client Component one.

From the React Query perspective, treat Server Components as a place to prefetch data, nothing more.

Expand Down Expand Up @@ -424,7 +432,7 @@ export function getQueryClient() {

> Note: This works in NextJs and Server Components because React can serialize Promises over the wire when you pass them down to Client Components.

Then, all we need to do is provide a `HydrationBoundary`, but we don't need to `await` prefetches anymore:
Then, all we need to do is provide a `HydrationBoundary`, but we don't need to `await` these prefetches anymore:

```tsx
// app/posts/page.tsx
Expand All @@ -437,10 +445,12 @@ export default function PostsPage() {
const queryClient = getQueryClient()

// look ma, no await
queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: getPosts,
})
void queryClient
.query({
queryKey: ['posts'],
queryFn: getPosts,
})
.catch(noop)

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand Down Expand Up @@ -504,10 +514,12 @@ export default function PostsPage() {
const queryClient = getQueryClient()

// look ma, no await
queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: () => getPosts().then(serialize), // <-- serialize the data on the server
})
void queryClient
.query({
queryKey: ['posts'],
queryFn: () => getPosts().then(serialize), // <-- serialize the data on the server
})
.catch(noop)

return (
<HydrationBoundary state={dehydrate(queryClient)}>
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/react/guides/initial-query-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ There are many ways to supply initial data for a query to the cache before you n
- Declaratively:
- Provide `initialData` to a query to prepopulate its cache if empty
- Imperatively:
- [Prefetch the data using `queryClient.prefetchQuery`](./prefetching.md)
- [Prefetch the data using `queryClient.query`](./prefetching.md)
- [Manually place the data into the cache using `queryClient.setQueryData`](./prefetching.md)

## Using `initialData` to prepopulate a query
Expand Down Expand Up @@ -84,7 +84,7 @@ By default, `initialData` is treated as totally fresh, as if it were just fetche

This option allows the staleTime to be used for its original purpose, determining how fresh the data needs to be, while also allowing the data to be refetched on mount if the `initialData` is older than the `staleTime`. In the example above, our data needs to be fresh within 1 minute, and we can hint to the query when the initialData was last updated so the query can decide for itself whether the data needs to be refetched again or not.

> If you would rather treat your data as **prefetched data**, we recommend that you use the `prefetchQuery` or `fetchQuery` APIs to populate the cache beforehand, thus letting you configure your `staleTime` independently from your initialData
> If you would rather treat your data as **prefetched data**, we recommend that you use the `query` api to populate the cache beforehand, thus letting you configure your `staleTime` independently from your `initialData`.

### Initial Data Function

Expand Down
47 changes: 36 additions & 11 deletions docs/framework/react/guides/migrating-to-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ useIsMutating({ mutationKey, ...filters }) // [!code ++]
```tsx
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
Expand All @@ -45,14 +43,6 @@ queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
```

```tsx
Expand All @@ -62,6 +52,39 @@ queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
```

### Imperative QueryClient methods

These methods are deprecated as of Tanstack Query `INSERT_FUTURE_V5_MINOR` and will be removed in v6.

If you are coming from v4 or earlier:

```tsx
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.query({ queryKey: key, queryFn: fn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.infiniteQuery({
queryKey: key,
queryFn: fn,
...options,
}) // [!code ++]

queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.query({ queryKey: key, queryFn: fn, ...options }).catch(noop) // [!code ++]

queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient
.infiniteQuery({ queryKey: key, queryFn: fn, ...options })
.catch(noop) // [!code ++]

queryClient.ensureQueryData(key, options) // [!code --]
queryClient.query({ queryKey: key, ...options, staleTime: 'static' }) // [!code ++]

queryClient.ensureInfiniteQueryData(key, options) // [!code --]
queryClient.infiniteQuery({ queryKey: key, ...options, staleTime: 'static' }) // [!code ++]
```

If you are updating older v5 code, It will be the same as the above except for keeping the single options object

Comment on lines +55 to +87
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Placeholder version number needs to be filled in.

Line 57 contains INSERT_FUTURE_V5_MINOR which should be replaced with the actual version number before merging.

Additionally, the migration examples are comprehensive and clearly show:

  • fetchQuery β†’ query
  • prefetchQuery β†’ query(...).catch(noop)
  • ensureQueryData β†’ query({ ..., staleTime: 'static' })
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/migrating-to-v5.md` around lines 55 - 87, Replace
the placeholder token INSERT_FUTURE_V5_MINOR in the sentence "deprecated as of
Tanstack Query INSERT_FUTURE_V5_MINOR" with the actual release version string
you are targeting (e.g., "v5.<minor>" or the exact minor version being
released); ensure the final sentence reads something like "deprecated as of
Tanstack Query v5.X" and leave the migration examples (fetchQuery β†’ query,
prefetchQuery β†’ query(...).catch(noop), ensureQueryData β†’ query({ ...,
staleTime: 'static' }), etc.) unchanged.

### `queryClient.getQueryData` now accepts queryKey only as an Argument

`queryClient.getQueryData` argument is changed to accept only a `queryKey`
Expand Down Expand Up @@ -494,7 +517,9 @@ Note that the infinite list must be bi-directional, which requires both `getNext

### Infinite Queries can prefetch multiple pages

Infinite Queries can be prefetched like regular Queries. Per default, only the first page of the Query will be prefetched and will be stored under the given QueryKey. If you want to prefetch more than one page, you can use the `pages` option. Read the [prefetching guide](./prefetching.md) for more information.
Infinite Queries can be prefetched like regular Queries. Per default, only the first page of the Query will be prefetched and will be stored under the given QueryKey. If you want to prefetch more than one page, you can use the `pages` option.

If you are updating older v5 examples, prefer `queryClient.infiniteQuery(...)` here instead of `queryClient.prefetchInfiniteQuery(...)`. Like the other legacy imperative methods, `prefetchInfiniteQuery` is deprecated as of `INSERT_FUTURE_V5_MINOR`. Read the [prefetching guide](./prefetching.md) for the current pattern.

### New `combine` option for `useQueries`

Expand Down
Loading
Loading