Skip to content

feat(query-core): add query() and infiniteQuery() methods#9835

Open
DogPawHat wants to merge 90 commits intoTanStack:mainfrom
DogPawHat:implement-simplified-imperitive-methods
Open

feat(query-core): add query() and infiniteQuery() methods#9835
DogPawHat wants to merge 90 commits intoTanStack:mainfrom
DogPawHat:implement-simplified-imperitive-methods

Conversation

@DogPawHat
Copy link
Copy Markdown
Contributor

@DogPawHat DogPawHat commented Nov 1, 2025

🎯 Changes

Implements #9135

  • adds query and infiniteQuery methods as replacements for fetchQuery/fetchInfiniteQuery
  • new methods respect select, enabled and queryFn === skipToken
  • throws errors if enabled === false or queryFn === skipToken and no cached data is able to be returned.
  • updates tests for QueryClient
    • existing scenarios for fetchQuery fetch tests plus the new scenarios introduced (enabled, select, skipToken, static staleTime)
  • adds tests for the queryOptions adaptors
  • updates to the react-query and preact-query prefetch hooks (prefetchQuery -> query)
  • added new methods to vue-query QueryClient subclass.
  • added deprecation tags to old methods (fetchQuery, prefetchQuery, ensureQueryData)
  • add new docs, updated prefetching guides

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • New Features

    • Added imperative query() and infiniteQuery() methods with preserved option semantics.
  • Documentation

    • Updated guides and examples to prefer query()/infiniteQuery(), showing migration notes and error‑handling patterns (void … .catch(noop)).
  • Types

    • Improved execute-option typings so shared options (select, enabled, page params) narrow returned data correctly.
  • Tests

    • Expanded type and runtime coverage validating new APIs, select/enabled behavior, and pageParam typing.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Nov 1, 2025

🦋 Changeset detected

Latest commit: fff81d9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
Name Type
@tanstack/react-query Minor
@tanstack/query-core Minor
@tanstack/vue-query Minor
@tanstack/react-query-devtools Major
@tanstack/react-query-next-experimental Major
@tanstack/react-query-persist-client Major
@tanstack/angular-query-experimental Patch
@tanstack/preact-query Patch
@tanstack/query-async-storage-persister Patch
@tanstack/query-broadcast-client-experimental Patch
@tanstack/query-persist-client-core Patch
@tanstack/query-sync-storage-persister Patch
@tanstack/solid-query Patch
@tanstack/svelte-query Patch
@tanstack/vue-query-devtools Major
@tanstack/angular-query-persist-client Patch
@tanstack/preact-query-devtools Patch
@tanstack/preact-query-persist-client Patch
@tanstack/solid-query-persist-client Patch
@tanstack/svelte-query-persist-client Patch
@tanstack/solid-query-devtools Patch
@tanstack/svelte-query-devtools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 1, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds new imperative methods QueryClient.query() and QueryClient.infiniteQuery() with execute option types, updates core types for infinite pages, implements runtime handling for enabled/staleTime/select, updates framework adapters to delegate to core, expands type and runtime tests, and updates docs/examples to prefer query/infiniteQuery (with .catch(noop) guidance).

Changes

Cohort / File(s) Summary
Core types
packages/query-core/src/types.ts
Add QueryExecuteOptions and InfiniteQueryExecuteOptions; rename FetchInfiniteQueryPagesInfiniteQueryPages; mark legacy Fetch/Ensure options deprecated.
Core implementation
packages/query-core/src/queryClient.ts
Add QueryClient.query(...) and QueryClient.infiniteQuery(...) implementations: normalize options, resolve enabled/staleTime, conditional fetch vs cache return, apply select; update JSDoc deprecations for legacy imperative APIs.
Core tests (types & runtime)
packages/query-core/src/__tests__/queryClient.test-d.tsx, packages/query-core/src/__tests__/queryClient.test.tsx
Add extensive type-tests for query/infiniteQuery (select, skipToken, enabled, pages/getNextPageParam) and runtime tests for staleTime: 'static', cache hits/misses, background revalidation, and prefetch semantics.
Framework adapters (Vue)
packages/vue-query/src/queryClient.ts, packages/vue-query/src/__tests__/queryClient.test.ts, packages/vue-query/src/__tests__/*
Add overloads for QueryExecuteOptions/InfiniteQueryExecuteOptions and MaybeRefDeep variants; implementations clone/unref options and delegate to core; tests assert unref behavior for options and callbacks.
Framework adapters (React/Preact/Solid/Svelte/Angular)
packages/*/src/*, packages/*/src/__tests__/*-d.tsx, packages/*/tests/*-d.ts
Add/adjust type tests and hook wrappers: change prefetch hooks to use execute-option types and call query/infiniteQuery (or fetchQuery where noted), update signatures/removals of UsePrefetchQueryOptions, and update tests verifying select inference, initialPageParam, and page typing.
Prefetch hooks & usages
packages/react-query/src/usePrefetchQuery.tsx, packages/react-query/src/usePrefetchInfiniteQuery.tsx, packages/preact-query/src/usePrefetchQuery.tsx, packages/preact-query/src/usePrefetchInfiniteQuery.tsx, packages/*/src/usePrefetch*.tsx
Change prefetch hooks to accept execute-option types and call client.query/client.infiniteQuery (or fetchQuery when preserving legacy behavior), explicitly ignore results and swallow errors with .then(noop).catch(noop).
Runtime tests (dedupe / suspension)
packages/react-query/src/__tests__/useQuery.promise.test.tsx
Add Suspense/deduplication test ensuring queryClient.query(...) does not duplicate queryFn while a component is suspended via query.promise.
Docs & examples
docs/**, docs/reference/QueryClient.md
Replace fetchQuery/prefetchQuery examples with query/infiniteQuery, add migration guidance and recommended .catch(noop) patterns for non-critical prefetches; remove some prefetch-specific docs/pages and update cross-links.
Changeset
.changeset/famous-owls-battle.md
Add changeset documenting the addition of query() and infiniteQuery() and minor release entries.
Misc tests & signature cleanups
packages/*/src/types.ts, packages/*/src/__tests__/*
Remove UsePrefetchQueryOptions from some packages, update types/tests accordingly; adjust negative type-test expectations around enabled/skipToken.

Sequence Diagram(s)

sequenceDiagram
  participant Client as QueryClient
  participant Cache as QueryCache
  participant Fetcher as QueryFn
  participant Selector as Select (optional)
  Client->>Cache: normalize options, resolve enabled & staleTime
  alt disabled && no cache
    Client-->>Client: reject (disabled + no data)
  else enabled && stale
    Client->>Fetcher: call queryFn (with pageParam if infinite)
    Fetcher-->>Client: data
    Client->>Cache: update cache entry
    alt select provided
      Client->>Selector: apply select(data)
      Selector-->>Client: selectedData
      Client-->>Caller: Promise resolves with selectedData
    else
      Client-->>Caller: Promise resolves with data
    end
  else not stale (cache hit)
    Cache-->>Client: cachedData
    alt select provided
      Client->>Selector: apply select(cachedQueryData)
      Selector-->>Client: selectedData
      Client-->>Caller: Promise resolves with selectedData
    else
      Client-->>Caller: Promise resolves with cachedData
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop and sniff the code anew,
Query and infinite join the crew,
Types aligned, tests take flight,
Docs refreshed — a tidy sight,
A rabbit's cheer for queries true!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main feature: adding query() and infiniteQuery() methods to QueryClient as new imperative APIs.
Description check ✅ Passed The PR description comprehensively covers all required sections: changes made, checklist items completed, and release impact confirmed with changeset generated.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Nov 1, 2025

View your CI Pipeline Execution ↗ for commit 2cd69aa

Command Status Duration Result
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 2s View ↗
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 31s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-08 16:54:22 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Nov 1, 2025

More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@9835

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@9835

@tanstack/preact-query

npm i https://pkg.pr.new/@tanstack/preact-query@9835

@tanstack/preact-query-devtools

npm i https://pkg.pr.new/@tanstack/preact-query-devtools@9835

@tanstack/preact-query-persist-client

npm i https://pkg.pr.new/@tanstack/preact-query-persist-client@9835

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@9835

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@9835

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@9835

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@9835

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@9835

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@9835

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@9835

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@9835

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@9835

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@9835

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@9835

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@9835

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@9835

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@9835

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@9835

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@9835

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@9835

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@9835

commit: 2cd69aa

@TkDodo
Copy link
Copy Markdown
Collaborator

TkDodo commented Nov 1, 2025

renames fetchQuery and fetchInfiniteQuery to query and infiniteQuery

sorry the idea was not to rename those, but to add new methods and deprecate the old ones. Otherwise, this would be a breaking change.

@codecov
Copy link
Copy Markdown

codecov bot commented Nov 1, 2025

Codecov Report

❌ Patch coverage is 75.00000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.62%. Comparing base (e907f89) to head (3b0207d).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #9835       +/-   ##
===========================================
+ Coverage   45.79%   59.62%   +13.82%     
===========================================
  Files         200      129       -71     
  Lines        8520     5754     -2766     
  Branches     1975     1587      -388     
===========================================
- Hits         3902     3431      -471     
+ Misses       4158     2006     -2152     
+ Partials      460      317      -143     
Components Coverage Δ
@tanstack/angular-query-experimental 93.85% <ø> (ø)
@tanstack/eslint-plugin-query ∅ <ø> (∅)
@tanstack/query-async-storage-persister 43.85% <ø> (ø)
@tanstack/query-broadcast-client-experimental 24.39% <ø> (ø)
@tanstack/query-codemods ∅ <ø> (∅)
@tanstack/query-core 97.34% <93.75%> (-0.04%) ⬇️
@tanstack/query-devtools 3.38% <ø> (ø)
@tanstack/query-persist-client-core 80.00% <ø> (ø)
@tanstack/query-sync-storage-persister 84.61% <ø> (ø)
@tanstack/query-test-utils ∅ <ø> (∅)
@tanstack/react-query 96.34% <ø> (ø)
@tanstack/react-query-devtools 9.25% <ø> (ø)
@tanstack/react-query-next-experimental ∅ <ø> (∅)
@tanstack/react-query-persist-client 100.00% <ø> (ø)
@tanstack/solid-query 77.81% <ø> (ø)
@tanstack/solid-query-devtools 64.17% <ø> (ø)
@tanstack/solid-query-persist-client 100.00% <ø> (ø)
@tanstack/svelte-query ∅ <ø> (∅)
@tanstack/svelte-query-devtools ∅ <ø> (∅)
@tanstack/svelte-query-persist-client ∅ <ø> (∅)
@tanstack/vue-query 71.34% <0.00%> (-0.58%) ⬇️
@tanstack/vue-query-devtools ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@DogPawHat DogPawHat force-pushed the implement-simplified-imperitive-methods branch from b3a20d4 to 46d274c Compare November 1, 2025 20:53
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 411f030 and 46d274c.

📒 Files selected for processing (5)
  • .changeset/wise-suns-ask.md (1 hunks)
  • packages/query-core/src/__tests__/queryClient.test-d.tsx (2 hunks)
  • packages/query-core/src/__tests__/queryClient.test.tsx (9 hunks)
  • packages/query-core/src/queryClient.ts (6 hunks)
  • packages/vue-query/src/queryClient.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
Repo: TanStack/query PR: 9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.

Applied to files:

  • .changeset/wise-suns-ask.md
🧬 Code graph analysis (4)
packages/query-core/src/__tests__/queryClient.test-d.tsx (3)
packages/query-core/src/types.ts (1)
  • InfiniteData (204-207)
packages/query-core/src/queryClient.ts (1)
  • infiniteQuery (395-417)
packages/vue-query/src/queryClient.ts (1)
  • infiniteQuery (328-346)
packages/query-core/src/__tests__/queryClient.test.tsx (2)
packages/query-core/src/utils.ts (2)
  • noop (82-82)
  • sleep (363-367)
packages/query-core/src/types.ts (1)
  • QueryFunction (96-100)
packages/vue-query/src/queryClient.ts (3)
packages/query-core/src/types.ts (5)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • FetchQueryOptions (491-507)
  • FetchInfiniteQueryOptions (548-565)
  • InfiniteData (204-207)
packages/vue-query/src/types.ts (1)
  • MaybeRefDeep (29-37)
packages/vue-query/src/utils.ts (1)
  • cloneDeepUnref (70-97)
packages/query-core/src/queryClient.ts (2)
packages/query-core/src/utils.ts (1)
  • noop (82-82)
packages/query-core/src/types.ts (5)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • FetchQueryOptions (491-507)
  • FetchInfiniteQueryOptions (548-565)
  • InfiniteData (204-207)
🪛 LanguageTool
.changeset/wise-suns-ask.md

[grammar] ~6-~6: Ensure spelling is correct
Context: ...tanstack/vue-query': minor --- renamed imperitive methods

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (8)
packages/query-core/src/queryClient.ts (5)

140-159: LGTM! ensureQueryData correctly delegates to the new query method.

The internal delegation to query with staleTime: 'static' maintains the expected behavior while utilizing the new method.


336-365: Verify alignment with maintainer expectations regarding API migration strategy.

Per TkDodo's comment, the original intent was to "add new methods and deprecate the old ones to avoid a breaking change, not to remove/rename them outright." While this implementation maintains backward compatibility by keeping fetchQuery as a wrapper, please confirm that making query the primary implementation (rather than keeping fetchQuery as primary and adding query as an alias) aligns with the team's migration strategy.


384-393: LGTM! prefetchQuery correctly delegates to the new query method.

The delegation pattern with .then(noop).catch(noop) properly maintains the fire-and-forget prefetch behavior.


395-417: LGTM! infiniteQuery implementation is clean and consistent.

The method correctly sets the infiniteQueryBehavior and delegates to the query method. The type assertion on line 416 is necessary due to the behavior transformation.


436-452: LGTM! prefetchInfiniteQuery correctly delegates to the new infiniteQuery method.

The delegation pattern maintains the fire-and-forget prefetch behavior.

packages/query-core/src/__tests__/queryClient.test-d.tsx (1)

313-328: LGTM! Type assertions for new methods are comprehensive and correct.

The type checks for query and infiniteQuery methods properly cover both fully typed and untyped scenarios, maintaining consistency with the existing test structure.

Also applies to: 462-477

packages/query-core/src/__tests__/queryClient.test.tsx (1)

11-11: LGTM! Comprehensive test coverage for the new query methods.

The test additions are excellent:

  • Properly imports noop utility for prefetch patterns
  • Marks old test suites with @deprecated comments for clarity
  • Provides thorough coverage of new query and infiniteQuery methods including:
    • Static staleTime behavior
    • Prefetching scenarios
    • Type safety validation
    • Edge cases (cache hits, invalidation, GC, etc.)
  • Maintains parallel structure with legacy tests while validating new functionality

Also applies to: 453-453, 529-620, 622-622, 683-720, 753-753, 928-1100, 1102-1102, 1147-1189, 1191-1191, 1281-1374, 1376-1376, 1426-1477

packages/vue-query/src/queryClient.ts (1)

256-346: LGTM! Vue Query extensions for new methods are well-implemented.

The query and infiniteQuery methods properly:

  • Provide overloads for both plain and MaybeRefDeep options
  • Use cloneDeepUnref to handle Vue's reactive refs correctly
  • Delegate to the parent QueryClient implementation
  • Follow the established pattern used by other methods in this class

@DogPawHat
Copy link
Copy Markdown
Contributor Author

DogPawHat commented Nov 1, 2025

From #9135:

Per default, this should behave like queryClient.fetchQuery does today:

  • it respects staleTime (like any good query should)
  • it returns a Promise you can await.

fetchQuery is still there, it's just a straight passthough for query now

@DogPawHat DogPawHat force-pushed the implement-simplified-imperitive-methods branch from c18dfc0 to 7670b40 Compare November 2, 2025 13:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16bed21 and fb9dcdc.

📒 Files selected for processing (8)
  • .changeset/famous-owls-battle.md (1 hunks)
  • packages/query-core/src/__tests__/queryClient.test-d.tsx (7 hunks)
  • packages/query-core/src/__tests__/queryClient.test.tsx (9 hunks)
  • packages/query-core/src/queryClient.ts (8 hunks)
  • packages/query-core/src/types.ts (3 hunks)
  • packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx (3 hunks)
  • packages/react-query/src/__tests__/queryOptions.test-d.tsx (2 hunks)
  • packages/vue-query/src/queryClient.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-query/src/tests/queryOptions.test-d.tsx
  • packages/react-query/src/tests/infiniteQueryOptions.test-d.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
Repo: TanStack/query PR: 9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.

Applied to files:

  • .changeset/famous-owls-battle.md
🧬 Code graph analysis (4)
packages/query-core/src/__tests__/queryClient.test-d.tsx (2)
packages/query-core/src/queryClient.ts (1)
  • infiniteQuery (409-431)
packages/query-core/src/types.ts (2)
  • InfiniteData (204-207)
  • InfiniteQueryExecuteOptions (568-586)
packages/query-core/src/queryClient.ts (4)
packages/query-core/src/utils.ts (2)
  • noop (82-82)
  • resolveStaleTime (101-113)
packages/query-core/src/types.ts (7)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • FetchQueryOptions (511-527)
  • InfiniteQueryExecuteOptions (568-586)
  • FetchInfiniteQueryOptions (588-605)
  • InfiniteData (204-207)
packages/query-core/src/query.ts (1)
  • isStale (296-306)
packages/vue-query/src/queryClient.ts (1)
  • query (294-314)
packages/query-core/src/__tests__/queryClient.test.tsx (2)
packages/query-core/src/utils.ts (2)
  • noop (82-82)
  • sleep (363-367)
packages/query-core/src/types.ts (1)
  • QueryFunction (96-100)
packages/vue-query/src/queryClient.ts (3)
packages/query-core/src/types.ts (5)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • InfiniteQueryExecuteOptions (568-586)
  • InfiniteData (204-207)
packages/vue-query/src/types.ts (1)
  • MaybeRefDeep (29-37)
packages/vue-query/src/utils.ts (1)
  • cloneDeepUnref (70-97)
🪛 Biome (2.1.2)
packages/query-core/src/__tests__/queryClient.test-d.tsx

[error] 342-342: Const declarations must have an initialized value.

This variable needs to be initialized.

(parse)


[error] 342-342: Shouldn't redeclare 'infiniteQueryOptions'. Consider to delete it or rename it.

'infiniteQueryOptions' is defined here:

(lint/suspicious/noRedeclare)


[error] 441-441: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 443-443: Shouldn't redeclare 'infiniteQuery'. Consider to delete it or rename it.

'infiniteQuery' is defined here:

(lint/suspicious/noRedeclare)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (7)
packages/query-core/src/__tests__/queryClient.test-d.tsx (7)

161-172: LGTM!

The test correctly verifies that fetchQuery does not accept the select option, maintaining backward compatibility as intended in the PR design.


174-227: LGTM!

The tests correctly verify:

  • select option is not allowed on fetchInfiniteQuery (backward compatibility)
  • pages parameter is properly typed
  • getNextPageParam is required when pages is specified

229-239: LGTM!

The test correctly verifies that the new query method accepts the select option, which is the key differentiator from the fetchQuery compatibility wrapper.


242-257: Verify whether select should be allowed on infiniteQuery.

This test asserts that select is not allowed on infiniteQuery, but:

  1. Lines 336-339 show InfiniteQueryExecuteOptions being used with a select option successfully
  2. Unlike the fetchInfiniteQuery test (line 183), there's no @ts-expect-error annotation on the select property (line 249)

Based on the implementation where infiniteQuery delegates to query (which supports select), it appears select should be allowed. Please clarify the intended behavior and add the @ts-expect-error annotation if select is truly not supported, or update the test description if it is supported.


428-429: LGTM!

The type check correctly verifies that query returns the expected TData type.


578-579: LGTM!

The type check correctly verifies that query returns unknown when called with untyped options.


583-593: LGTM!

The type checks correctly verify the return types for fetchInfiniteQuery and infiniteQuery with untyped options, both returning InfiniteData<unknown, unknown>.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb9dcdc and 3b33d44.

📒 Files selected for processing (2)
  • .changeset/famous-owls-battle.md (1 hunks)
  • packages/query-core/src/__tests__/queryClient.test-d.tsx (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/famous-owls-battle.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
🧬 Code graph analysis (1)
packages/query-core/src/__tests__/queryClient.test-d.tsx (3)
packages/query-core/src/queryClient.ts (2)
  • QueryClient (63-692)
  • infiniteQuery (409-431)
packages/vue-query/src/queryClient.ts (2)
  • QueryClient (39-589)
  • infiniteQuery (348-366)
packages/query-core/src/types.ts (2)
  • InfiniteData (204-207)
  • InfiniteQueryExecuteOptions (568-586)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b33d44 and a24433b.

📒 Files selected for processing (1)
  • packages/query-core/src/__tests__/queryClient.test-d.tsx (7 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.049Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.
📚 Learning: 2025-11-02T22:52:33.049Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.049Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
🧬 Code graph analysis (1)
packages/query-core/src/__tests__/queryClient.test-d.tsx (2)
packages/query-core/src/queryClient.ts (1)
  • infiniteQuery (409-431)
packages/query-core/src/types.ts (2)
  • InfiniteData (204-207)
  • InfiniteQueryExecuteOptions (568-586)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (5)
packages/query-core/src/__tests__/queryClient.test-d.tsx (5)

13-13: LGTM!

The addition of InfiniteQueryExecuteOptions to the imports aligns with the new public API surface introduced in this PR.


161-172: LGTM!

This test correctly validates that the legacy fetchQuery method does not support the select option, with proper use of @ts-expect-error to enforce the type constraint.


174-227: LGTM!

The tests for fetchInfiniteQuery correctly validate the legacy behavior, including:

  • Properly rejecting select option with @ts-expect-error
  • Validating the pages option with correct type expectations
  • Enforcing required parameter constraints

229-239: LGTM!

This test correctly validates that the new query method supports the select option for data transformation, properly demonstrating the enhanced functionality compared to the legacy fetchQuery.


328-336: LGTM!

The integration of the new query and infiniteQuery methods into the fully-typed and untyped test sections is correct:

  • Proper usage of InfiniteQueryExecuteOptions<TData, TError> type
  • Correct type expectations for return values
  • Good coverage of both typed and untyped variants

Also applies to: 422-423, 434-435, 569-570, 581-584

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/query-core/src/__tests__/queryClient.test-d.tsx (1)

205-214: Fix misleading test title.

The test uses assertType without @ts-expect-error, meaning it's verifying that passing getNextPageParam without pages is allowed (per the InfiniteQueryPages union's { pages?: never } branch). The title incorrectly says "should not allow".

Apply this diff:

-  it('should not allow passing getNextPageParam without pages', () => {
+  it('should allow passing getNextPageParam without pages', () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a24433b and fa6f86f.

📒 Files selected for processing (2)
  • packages/query-core/src/__tests__/queryClient.test-d.tsx (7 hunks)
  • packages/query-core/src/types.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.049Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.
📚 Learning: 2025-11-02T22:52:33.049Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.049Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
  • packages/query-core/src/types.ts
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/__tests__/queryClient.test-d.tsx
  • packages/query-core/src/types.ts
🧬 Code graph analysis (1)
packages/query-core/src/__tests__/queryClient.test-d.tsx (2)
packages/query-core/src/queryClient.ts (1)
  • infiniteQuery (409-431)
packages/query-core/src/types.ts (2)
  • InfiniteData (204-207)
  • InfiniteQueryExecuteOptions (568-586)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (7)
packages/query-core/src/types.ts (3)

491-509: LGTM: QueryExecuteOptions introduces select support correctly.

The new QueryExecuteOptions interface properly introduces the select transformation flow via the TQueryData generic. The data flows as TQueryFnData (raw) → TQueryData (intermediate) → TData (final via select). The staleTime correctly uses TQueryData since staleness should be determined from the query's stored data, not the transformed result.


511-528: LGTM: FetchQueryOptions maintains backward compatibility.

The refactor to make FetchQueryOptions an alias that omits select from QueryExecuteOptions is correct. Passing TData as both the 3rd and 4th generics (TData, TData) ensures no transformation layer exists when select is omitted, maintaining the original behavior of fetchQuery.


568-586: LGTM: InfiniteQueryExecuteOptions properly extends execute options.

The type correctly:

  • Omits initialPageParam from QueryExecuteOptions (which has initialPageParam?: never)
  • Re-adds it via InitialPageParam<TPageParam> with the proper type
  • Uses InfiniteData<TQueryFnData, TPageParam> as the intermediate data shape
  • Includes InfiniteQueryPages discriminated union to enforce the pages/getNextPageParam constraint
packages/query-core/src/__tests__/queryClient.test-d.tsx (4)

161-172: LGTM: Correctly verifies fetchQuery rejects select.

The test properly uses @ts-expect-error to confirm that the legacy fetchQuery method does not support the select option, maintaining backward compatibility.

Based on learnings.


229-239: LGTM: Correctly verifies query accepts select.

The test confirms that the new query method supports the select option for data transformation, using assertType (without @ts-expect-error) to validate this is allowed.

Based on learnings.


241-257: LGTM: Correctly verifies infiniteQuery accepts select.

The test confirms that the new infiniteQuery method supports the select option for transforming infinite query data, using assertType (without @ts-expect-error) to validate this is allowed.

Based on learnings.


328-336: LGTM: infiniteQueryOptions properly typed.

The variable correctly uses InfiniteQueryExecuteOptions<TData, TError> with defaults for remaining generics. The getNextPageParam callback properly receives lastPage typed as TData.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/query-core/src/queryClient.ts (1)

428-450: Implementation delegates correctly, but consider type-safe alternatives to the cast.

The method properly sets infiniteQueryBehavior before delegating to query. The as any cast on line 449 bypasses type safety—consider whether tighter generic constraints or a helper could eliminate it.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa6f86f and 6a3c2ed.

📒 Files selected for processing (2)
  • packages/query-core/src/queryClient.ts (5 hunks)
  • packages/query-core/src/types.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.
📚 Learning: 2025-11-02T22:52:33.071Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/query-core/src/types.ts
  • packages/query-core/src/queryClient.ts
🧬 Code graph analysis (1)
packages/query-core/src/queryClient.ts (3)
packages/query-core/src/types.ts (6)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • InfiniteQueryExecuteOptions (568-586)
  • FetchInfiniteQueryOptions (588-605)
  • InfiniteData (204-207)
packages/vue-query/src/queryClient.ts (1)
  • query (294-314)
packages/query-core/src/utils.ts (1)
  • resolveStaleTime (101-113)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (4)
packages/query-core/src/types.ts (2)

491-509: LGTM! Well-designed interface for query execution options.

The interface correctly separates TQueryData from TData to enable optional select transformation, explicitly prevents initialPageParam for single queries, and provides clear documentation for staleTime behavior.


561-586: LGTM! Clean type composition for infinite query execution.

The discriminated union for InfiniteQueryPages and the composition strategy for InfiniteQueryExecuteOptions (omitting the never field then adding the required version) correctly enforces that infinite queries must provide initialPageParam.

packages/query-core/src/queryClient.ts (2)

343-384: LGTM! Core query execution logic is sound.

The method correctly handles stale-time checking, optional data fetching, and select transformation. The double cast as unknown as TData on line 383 is a TypeScript workaround when no select is provided (TData defaults to TQueryData), though it's safe in practice.


452-468: Fix: Missing options.behavior setup before delegation to fetchQuery.

The fetchInfiniteQuery method must set options.behavior before delegating to fetchQuery, matching the pattern used in infiniteQuery (lines 443-448) and ensureInfiniteQueryData (lines 503-508). Without this, infinite query pagination will not work correctly.

 fetchInfiniteQuery<
   TQueryFnData,
   TError = DefaultError,
   TData = TQueryFnData,
   TQueryKey extends QueryKey = QueryKey,
   TPageParam = unknown,
 >(
   options: FetchInfiniteQueryOptions<
     TQueryFnData,
     TError,
     TData,
     TQueryKey,
     TPageParam
   >,
 ): Promise<InfiniteData<TData, TPageParam>> {
+  options.behavior = infiniteQueryBehavior<
+    TQueryFnData,
+    TError,
+    TData,
+    TPageParam
+  >(options.pages)
   return this.fetchQuery(options as any)
 }
⛔ Skipped due to learnings
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/query-core/src/queryClient.ts (1)

428-450: Fix the type error and avoid as any assertion.

Two issues need to be addressed:

  1. Pipeline failure (Line 443): The type error indicates that QueryBehavior<TQueryFnData, TError, InfiniteData<TQueryFnData, TPageParam>, readonly unknown[]> is not assignable to QueryBehavior<TQueryFnData, TError, InfiniteData<TQueryFnData, TPageParam>, TQueryKey>. This suggests a mismatch in the query key type constraint.

  2. Line 449: The as any type assertion bypasses type safety and hides potential type errors. This indicates that the types don't align properly between InfiniteQueryExecuteOptions and QueryExecuteOptions.

The type system should properly model the relationship between these option types without requiring unsafe casts. Please investigate:

  • Whether the QueryExecuteOptions type needs to be adjusted to properly accept infinite query options
  • Whether the generic constraints need to be refined to make the types compatible
  • Alternative approaches that maintain type safety
#!/bin/bash
# Check how behavior property is typed and used
ast-grep --pattern $'behavior: QueryBehavior<$$$>'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a3c2ed and 1338e8e.

📒 Files selected for processing (1)
  • packages/query-core/src/queryClient.ts (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.
📚 Learning: 2025-11-02T22:52:33.071Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/query-core/src/queryClient.ts
🧬 Code graph analysis (1)
packages/query-core/src/queryClient.ts (3)
packages/query-core/src/types.ts (6)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • InfiniteQueryExecuteOptions (568-586)
  • FetchInfiniteQueryOptions (588-605)
  • InfiniteData (204-207)
packages/vue-query/src/queryClient.ts (1)
  • query (294-314)
packages/query-core/src/utils.ts (1)
  • resolveStaleTime (101-113)
🪛 GitHub Actions: PR
packages/query-core/src/queryClient.ts

[error] 443-443: Type 'QueryBehavior<TQueryFnData, TError, InfiniteData<TQueryFnData, TPageParam>, readonly unknown[]>' is not assignable to type 'QueryBehavior<TQueryFnData, TError, InfiniteData<TQueryFnData, TPageParam>, TQueryKey>'.

🔇 Additional comments (2)
packages/query-core/src/queryClient.ts (2)

28-28: LGTM!

The new type imports support the query and infiniteQuery method signatures correctly.

Also applies to: 37-37


373-383: Verify data handling assumptions in the query method implementation.

The technical concerns in the review are valid: when isStale is false, query.state.data could be undefined (initialized without initialData), and this would be passed to the select function without a guard. The type signature select?: (data: TQueryData) => TData expects defined data, but the cast on line 375 (as TQueryData) doesn't guarantee it at runtime.

However, test cases across the codebase show select always receives properly typed, defined data—no scenario in tests passes undefined to the selector. This suggests either:

  1. The scenario is prevented by caller contracts (e.g., always providing initialData or ensuring fetch completes first)
  2. The stale-time logic prevents isStale=false when data is undefined
  3. This is an edge case not covered by tests

Confirm whether adding a guard (e.g., if (!queryData) return queryData as TData) is necessary, or if the current design intentionally relies on caller responsibility to provide valid initial state.

@DogPawHat DogPawHat force-pushed the implement-simplified-imperitive-methods branch from 1338e8e to 8c46d9e Compare November 26, 2025 14:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/query-core/src/queryClient.ts (1)

428-452: Restore infiniteQueryBehavior wiring for fetchInfiniteQuery and reduce as any surface

Currently, only infiniteQuery and ensureInfiniteQueryData attach infiniteQueryBehavior, while fetchInfiniteQuery now delegates straight to fetchQuery without setting options.behavior:

fetchInfiniteQuery(...) {
  return this.fetchQuery(options as any)
}

In the published build, fetchInfiniteQuery sets options.behavior = infiniteQueryBehavior(options.pages) before calling fetchQuery, which is what gives it proper infinite paging semantics (using pages, pageParams, and getNextPageParam). (impactic.de) Dropping that here likely breaks existing fetchInfiniteQuery behavior and will make tests like “should return infinite query data”/“should prefetch multiple pages” incorrect at runtime.

I’d strongly suggest restoring the behavior setup, e.g.:

  fetchInfiniteQuery<
    TQueryFnData,
    TError = DefaultError,
    TData = TQueryFnData,
    TQueryKey extends QueryKey = QueryKey,
    TPageParam = unknown,
  >(
    options: FetchInfiniteQueryOptions<
      TQueryFnData,
      TError,
      TData,
      TQueryKey,
      TPageParam
    >,
  ): Promise<InfiniteData<TData, TPageParam>> {
    options.behavior = infiniteQueryBehavior<
      TQueryFnData,
      TError,
      TData,
      TPageParam
    >(options.pages)
    return this.fetchQuery(options as any)
  }

On top of that, the three as any casts (this.query(options as any), this.fetchQuery(options as any), this.ensureQueryData(options as any)) are understandable given the generic differences between execute/fetch option types, but they do punch a hole through type safety. If feasible, consider a small internal helper or tightening the generic relationships between QueryExecuteOptions, InfiniteQueryExecuteOptions, FetchQueryOptions, and FetchInfiniteQueryOptions so these calls can be expressed without any (or at least with a narrower cast and a brief comment on why it’s safe).

Also applies to: 470-486, 488-511

🧹 Nitpick comments (1)
packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx (1)

53-66: Type tests correctly encode select behavior for infinite paths

The new tests nicely capture the intended distinctions:

  • useInfiniteQuery, useSuspenseInfiniteQuery, and QueryClient.infiniteQuery all see select and return the transformed Array<string> type.
  • fetchInfiniteQuery still yields InfiniteData<string, number> even when given options that include select, matching the legacy “no select” behavior for fetch APIs.

Only tiny nit: the “known issue” comment in the suspense test still mentions useInfiniteQuery, but that’s cosmetic.

Also applies to: 79-92, 93-117, 130-142

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1338e8e and 8c46d9e.

📒 Files selected for processing (8)
  • .changeset/famous-owls-battle.md (1 hunks)
  • packages/query-core/src/__tests__/queryClient.test-d.tsx (7 hunks)
  • packages/query-core/src/__tests__/queryClient.test.tsx (9 hunks)
  • packages/query-core/src/queryClient.ts (5 hunks)
  • packages/query-core/src/types.ts (3 hunks)
  • packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx (3 hunks)
  • packages/react-query/src/__tests__/queryOptions.test-d.tsx (2 hunks)
  • packages/vue-query/src/queryClient.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/famous-owls-battle.md
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.
📚 Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.

Applied to files:

  • packages/react-query/src/__tests__/queryOptions.test-d.tsx
  • packages/query-core/src/__tests__/queryClient.test-d.tsx
  • packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx
  • packages/query-core/src/__tests__/queryClient.test.tsx
  • packages/query-core/src/queryClient.ts
  • packages/vue-query/src/queryClient.ts
📚 Learning: 2025-11-02T22:52:33.071Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/react-query/src/__tests__/queryOptions.test-d.tsx
  • packages/query-core/src/__tests__/queryClient.test-d.tsx
  • packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx
  • packages/query-core/src/__tests__/queryClient.test.tsx
  • packages/query-core/src/queryClient.ts
  • packages/vue-query/src/queryClient.ts
  • packages/query-core/src/types.ts
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/react-query/src/__tests__/queryOptions.test-d.tsx
  • packages/query-core/src/__tests__/queryClient.test-d.tsx
  • packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx
  • packages/query-core/src/queryClient.ts
🧬 Code graph analysis (3)
packages/query-core/src/__tests__/queryClient.test-d.tsx (1)
packages/query-core/src/types.ts (2)
  • InfiniteData (204-207)
  • InfiniteQueryExecuteOptions (568-586)
packages/query-core/src/queryClient.ts (2)
packages/query-core/src/types.ts (6)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • InfiniteQueryExecuteOptions (568-586)
  • FetchInfiniteQueryOptions (588-605)
  • InfiniteData (204-207)
packages/query-core/src/utils.ts (1)
  • resolveStaleTime (112-124)
packages/vue-query/src/queryClient.ts (3)
packages/query-core/src/types.ts (5)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • InfiniteQueryExecuteOptions (568-586)
  • InfiniteData (204-207)
packages/vue-query/src/types.ts (1)
  • MaybeRefDeep (29-37)
packages/vue-query/src/utils.ts (1)
  • cloneDeepUnref (70-97)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (8)
packages/query-core/src/types.ts (1)

491-509: New execute option types align well with observer options and infinite semantics

QueryExecuteOptions and InfiniteQueryExecuteOptions are wired correctly: using TQueryData for staleTime and select matches how defaultQueryOptions and resolveStaleTime operate, and InfiniteQueryPages cleanly enforces the pages/getNextPageParam pairing while being reused by both execute and fetch infinite options. This should give query/infiniteQuery the right type-level behavior without impacting existing Fetch* callers.

Also applies to: 561-587, 588-605

packages/react-query/src/__tests__/queryOptions.test-d.tsx (1)

58-66: query vs fetchQuery select behavior is well covered

The added tests clearly distinguish the new query API (including select transformations) from the legacy fetchQuery (which returns the raw number even when given options that contain select). This matches the intended separation between the new and old imperative entry points.

Also applies to: 76-95

packages/query-core/src/__tests__/queryClient.test-d.tsx (2)

161-172: Type tests cleanly separate select support between legacy and new APIs

The new blocks for fetchQuery, fetchInfiniteQuery, query, and infiniteQuery correctly encode the contract:

  • fetchQuery/fetchInfiniteQuery reject select at the option level via @ts-expect-error.
  • query/infiniteQuery accept select and assert the transformed data types.

This gives solid type-level coverage for the migration story from the old fetch* APIs to the new imperative methods.

Also applies to: 175-192, 229-257


193-203: Infinite execute/fetch options generics are exercised thoroughly

The updated tests around passing pages, using { count: number } pages, and the “fully typed usage” blocks validate that:

  • FetchInfiniteQueryOptions and InfiniteQueryExecuteOptions expose lastPage as TData in getNextPageParam.
  • infiniteQuery and fetchInfiniteQuery both resolve to InfiniteData<TData, TPageParam> (or InfiniteData<unknown, unknown> in the untyped case).

This gives good confidence that the new infinite-execute types integrate correctly with QueryClient’s generics.

Also applies to: 259-269, 328-337, 422-435, 569-585

packages/query-core/src/__tests__/queryClient.test.tsx (2)

529-620: Static staleTime behavior for query/infiniteQuery is well specified

The new “with static staleTime” suites for both query and infiniteQuery clearly pin down the behavior:

  • Cached data (including falsy values) is returned without re-fetching when staleTime is 'static'.
  • Missing queries invoke queryFn once.
  • Providing initialData short-circuits network calls.
  • A second call with staleTime: 0 drives an explicit background revalidation, as verified via noop and cache assertions.

This is a strong regression suite for the new imperative surface.

Also applies to: 683-720


928-1106: Runtime tests give good confidence in new query/infiniteQuery entry points

The added query/infiniteQuery suites (including “used for prefetching”) thoroughly mirror the legacy fetchQuery/prefetch* behavior:

  • No-retry-by-default semantics, gcTime handling, and static staleTime cache hits are preserved.
  • Infinite flows still produce the expected InfiniteData shapes, including multi-page prefetch and early termination via getNextPageParam.
  • Prefetch-style usages (.catch(noop)) ensure that using query/infiniteQuery purely for seeding the cache continues to work.

Overall, the tests look well aligned with the intended migration path from the deprecated fetch* methods to the new APIs.

Also applies to: 1153-1195, 1287-1380, 1432-1483

packages/vue-query/src/queryClient.ts (1)

19-37: Vue QueryClient query/infiniteQuery wrappers are consistent and type-safe

The new query and infiniteQuery overloads:

  • Reuse the core QueryExecuteOptions / InfiniteQueryExecuteOptions generics correctly.
  • Preserve the Vue adapter’s MaybeRefDeep + cloneDeepUnref pattern so refs and getter-based queryKeys are handled before delegating.
  • Return the expected Promise<TData> / Promise<InfiniteData<TData, TPageParam>> types, matching the core client.

This keeps the Vue surface in lockstep with the new imperative APIs.

Also applies to: 258-366

packages/query-core/src/queryClient.ts (1)

343-384: query implementation correctly layers select on top of legacy fetch semantics

The new query method:

  • Runs through defaultQueryOptions, preserves the “no retry by default” behavior from fetchQuery, and uses resolveStaleTime with QueryExecuteOptions.staleTime typed on TQueryData.
  • Fetches when isStaleByTime is true, otherwise returns query.state.data from cache.
  • Applies select once, purely at the imperative layer, without mutating the stored query data.

This matches the intended contract for the new imperative API and is well covered by the added runtime and type tests.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/query-core/src/queryClient.ts (1)

428-454: Type assertion bypasses type safety.

Line 453 uses as any when delegating to this.query, which bypasses TypeScript's type checking. While this mirrors the existing pattern in fetchInfiniteQuery (Line 477), the cast suggests that InfiniteQueryExecuteOptions and QueryExecuteOptions aren't properly aligned.

Consider refactoring to eliminate the cast by:

  • Ensuring type compatibility between the two option types
  • Extracting a shared internal helper that both methods can call with appropriate generic bounds
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c46d9e and a16e585.

📒 Files selected for processing (3)
  • packages/query-core/src/queryClient.ts (4 hunks)
  • packages/query-core/src/types.ts (3 hunks)
  • packages/vue-query/src/queryClient.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.
📚 Learning: 2025-11-02T22:52:33.071Z
Learnt from: DogPawHat
Repo: TanStack/query PR: 9835
File: packages/query-core/src/__tests__/queryClient.test-d.tsx:242-256
Timestamp: 2025-11-02T22:52:33.071Z
Learning: In the TanStack Query codebase, the new `query` and `infiniteQuery` methods support the `select` option for data transformation, while the legacy `fetchQuery` and `fetchInfiniteQuery` methods do not support `select` and should reject it at the type level.

Applied to files:

  • packages/query-core/src/queryClient.ts
  • packages/query-core/src/types.ts
  • packages/vue-query/src/queryClient.ts
📚 Learning: 2025-11-22T09:06:05.219Z
Learnt from: sukvvon
Repo: TanStack/query PR: 9892
File: packages/solid-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx:331-335
Timestamp: 2025-11-22T09:06:05.219Z
Learning: In TanStack/query test files, when a queryFn contains side effects (e.g., setting flags for test verification), prefer async/await syntax for clarity; when there are no side effects, prefer the .then() pattern for conciseness.

Applied to files:

  • packages/query-core/src/queryClient.ts
  • packages/vue-query/src/queryClient.ts
📚 Learning: 2025-08-19T03:18:18.303Z
Learnt from: oscartbeaumont
Repo: TanStack/query PR: 9564
File: packages/solid-query-devtools/src/production.tsx:2-3
Timestamp: 2025-08-19T03:18:18.303Z
Learning: In the solid-query-devtools package, the codebase uses a pattern of type-only default imports combined with typeof for component type annotations (e.g., `import type SolidQueryDevtoolsComp from './devtools'` followed by `typeof SolidQueryDevtoolsComp`). This pattern is consistently used across index.tsx and production.tsx files, and the maintainers prefer consistency over changing this approach.

Applied to files:

  • packages/query-core/src/queryClient.ts
🧬 Code graph analysis (1)
packages/query-core/src/queryClient.ts (4)
packages/query-core/src/types.ts (5)
  • DefaultError (47-51)
  • QueryKey (53-61)
  • QueryExecuteOptions (491-509)
  • InfiniteData (204-207)
  • InfiniteQueryExecuteOptions (568-586)
packages/query-core/src/query.ts (1)
  • isStale (296-306)
packages/query-core/src/utils.ts (1)
  • resolveStaleTime (112-124)
packages/query-core/src/infiniteQueryBehavior.ts (1)
  • infiniteQueryBehavior (11-133)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (2)
packages/query-core/src/queryClient.ts (1)

343-384: LGTM! The query method logic is sound.

The implementation correctly:

  • Defaults retry to false (matching fetchQuery behavior)
  • Checks staleness via resolveStaleTime
  • Fetches when stale or returns cached data
  • Applies select transformation when provided

The double cast at Line 383 (as unknown as TData) is safe given that TData defaults to TQueryData when select is not provided, though it's overly defensive.

Based on learnings, the new query method correctly supports the select option for data transformation.

packages/query-core/src/types.ts (1)

491-509: LGTM! Type definitions are well-structured.

The new type definitions are correctly designed:

  • QueryExecuteOptions (491-509): Properly extends QueryOptions with required queryKey, adds select for data transformation, and includes staleTime for staleness control
  • InfiniteQueryPages (561-566): Clean discriminated union that allows optional pages specification or requires getNextPageParam when pages are specified
  • InfiniteQueryExecuteOptions (568-586): Well-composed type combining QueryExecuteOptions, InitialPageParam, and InfiniteQueryPages
  • FetchInfiniteQueryOptions (605): Updated to reuse InfiniteQueryPages, improving consistency

Based on learnings, the new types correctly support the select option for data transformation in query and infiniteQuery methods.

Also applies to: 561-586, 605-605

TQueryFnData,
TError = DefaultError,
TData = TQueryFnData,
TData = InfiniteData<TQueryFnData>,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This seems to match the options used by react query queryOptions but not InfiniteQueryObserverOptions in query-core types.ts.

Copy link
Copy Markdown
Contributor Author

@DogPawHat DogPawHat left a comment

Choose a reason for hiding this comment

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

Pushed up the docs rewrite, new tests, deprecation notices, and a few misc changes regarding things like usePrefetchQuery

Self-review comments below:


### Imperative QueryClient methods

These methods are deprecated as of Tanstack Query `INSERT_FUTURE_V5_MINOR` and will be removed in v6.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I know this is a janky way of doing this. Open to suggestions or just saying "these methods are now deprecated"


## prefetchQuery & prefetchInfiniteQuery
## Using `query` to prefetch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

might need @Ephem to take a look at the prefetching guide changes and make sure it makes sense.


Its available methods are:

- [`queryClient.fetchQuery`](#queryclientfetchquery)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should we have a section for these deprecated methods?

**Options**

The options for `fetchQuery` are exactly the same as those of [`useQuery`](../framework/react/reference/useQuery.md), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, refetchOnMount, notifyOnChangeProps, throwOnError, select, suspense, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/TanStack/query/blob/7cd2d192e6da3df0b08e334ea1cf04cd70478827/packages/query-core/src/types.ts#L119) for more clarity.
The options for `query` are exactly the same as those of [`useQuery`](../framework/react/reference/useQuery.md), except the following: `refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, refetchOnMount, notifyOnChangeProps, throwOnError, suspense, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/TanStack/query/blob/7cd2d192e6da3df0b08e334ea1cf04cd70478827/packages/query-core/src/types.ts#L119) for more clarity.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Need to update the link to the source code

| { pages?: never }
| {
pages: number
getNextPageParam: GetNextPageParamFunction<TPageParam, TQueryFnData>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

getPreviousPageParam is not in the types for fetchInfiniteQuery/inifiniteQuery as it turns out. Should it be added in to the types here?


if (!client.getQueryState(options.queryKey)) {
client.prefetchQuery(options)
void client.query(options).then(noop).catch(noop)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These prefetch hooks are the only direct use the adapter packages (react-query and preact-query) make of query, hense the inclusion of those in the change set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm, wouldn't the check for getQueryState be doing the same thing as staleTime: 'static' here?

@DogPawHat
Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/framework/vue/guides/ssr.md (1)

58-65: ⚠️ Potential issue | 🟠 Major

suspense is undefined in this Nuxt 3 snippet.

Line 65 still calls await suspense(), but Line 59 now only destructures { data } from useQuery(...). This example no longer works as written. Either restore suspense in the destructuring or switch onServerPrefetch to await queryClient.query(...) if the goal is to show the new imperative API.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/vue/guides/ssr.md` around lines 58 - 65, The snippet calls
await suspense() but no longer destructures suspense from useQuery; fix by
either restoring suspense in the useQuery destructure (e.g., const { data,
suspense } = useQuery(...)) or replace the onServerPrefetch body to use the
imperative API via the query client (e.g., call await queryClient.query(...) for
the same queryKey/queryFn) so onServerPrefetch no longer references an undefined
suspense; update the example accordingly using the symbols useQuery, suspense,
onServerPrefetch, and queryClient.query to locate the code to change.
♻️ Duplicate comments (10)
docs/framework/solid/guides/query-options.md (1)

35-35: ⚠️ Potential issue | 🟡 Minor

Handle fire-and-forget rejection in the example.

At Line 35, the bare queryClient.query(...) call can reject and surface as an unhandled promise when copied into app code.

Suggested fix
-queryClient.query(groupOptions(23))
+queryClient.query(groupOptions(23)).catch(() => {})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/query-options.md` at line 35, The example calls
queryClient.query(groupOptions(23)) without handling rejections; update the
example to handle promise rejection from queryClient.query (or use await inside
an async context) by adding proper error handling (e.g., awaiting the call or
attaching .catch) around the queryClient.query invocation so failures from
groupOptions(23) or queryClient.query are not left as unhandled promise
rejections; locate the invocation of queryClient.query and change it to an
awaited call or add a .catch handler.
docs/framework/react/guides/query-options.md (1)

28-28: ⚠️ Potential issue | 🟡 Minor

Catch the promise in this fire-and-forget example.

At Line 28, this call can reject and lead to unhandled promise rejections when copied directly.

Suggested fix
-queryClient.query(groupOptions(23))
+queryClient.query(groupOptions(23)).catch(() => {})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/query-options.md` at line 28, The example calling
queryClient.query(groupOptions(23)) is a fire-and-forget promise that can
reject; update the example to handle rejections by either awaiting the call
inside an async function with try/catch or by attaching a .catch handler to the
returned promise (i.e., ensure queryClient.query(...) is awaited inside an async
function or followed by .catch(err => /* log/handle */)), referencing the same
queryClient.query and groupOptions symbols so the example demonstrates safe
error handling.
docs/framework/solid/typescript.md (1)

190-190: ⚠️ Potential issue | 🟡 Minor

Add rejection handling for the example queryClient.query call.

Line 190 is still a fire-and-forget Promise without a rejection handler, which can produce unhandled rejections in copied snippets.

Suggested fix
-queryClient.query(groupOptions())
+void queryClient.query(groupOptions()).catch(() => {})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/typescript.md` at line 190, The example call to
queryClient.query(groupOptions()) is a fire-and-forget Promise and must handle
rejections; update the snippet to either await the call inside an async function
or attach a .catch handler to queryClient.query(...) to handle and log errors
(reference: queryClient.query and groupOptions) so the example doesn't produce
unhandled promise rejections.
docs/framework/react/guides/migrating-to-v5.md (1)

57-57: ⚠️ Potential issue | 🟡 Minor

Replace unreconciled placeholder token before merge.

INSERT_FUTURE_V5_MINOR at Line 57 should be replaced with the actual release version (or neutral wording that doesn’t require a hardcoded placeholder).

Proposed wording
-These methods are deprecated as of Tanstack Query `INSERT_FUTURE_V5_MINOR` and will be removed in v6.
+These methods are deprecated as of a later TanStack Query v5 minor release and will be removed in v6.
🤖 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` at line 57, Replace the
unresolved placeholder token INSERT_FUTURE_V5_MINOR in the sentence "These
methods are deprecated as of Tanstack Query `INSERT_FUTURE_V5_MINOR` and will be
removed in v6." with a concrete version identifier or neutral wording; locate
the string INSERT_FUTURE_V5_MINOR in the migrating-to-v5.md content and either
substitute it with the actual release version (e.g., `v5.x.y`) or reword to
something like "a future v5 minor release" so the documentation no longer
contains the placeholder.
docs/framework/svelte/ssr.md (1)

10-10: ⚠️ Potential issue | 🟠 Major

queryClient.query SSR guidance is still behaviorally incorrect with enabled: browser.

Line 10 says queryClient.query() is not disabled, but with defaultOptions.queries.enabled = browser, server-side query() is gated unless explicitly overridden. The current snippet can reject and get silently swallowed by .catch(noop), making the cache guarantee at Line 140 unreliable.

Proposed doc fix
-The recommended way to achieve this is to use the `browser` module from SvelteKit in your `QueryClient` object. This will not disable `queryClient.query()`, which is used in one of the solutions below.
+The recommended way to achieve this is to use the `browser` module from SvelteKit in your `QueryClient` object. This disables observer-based queries during SSR; for imperative preloading with `queryClient.query`, override `enabled: true` in that specific call.
   await queryClient
     .query({
       queryKey: ['posts'],
       queryFn: async () => (await fetch('/api/posts')).json(),
+      enabled: true,
     })
     .catch(noop)
-  // This data is cached by query in +page.ts so no fetch actually happens here
+  // This data is cached by query in +page.ts (with enabled: true) so no fetch actually happens here

Run this read-only check to confirm current behavior in core:

#!/bin/bash
set -euo pipefail

# 1) Inspect QueryClient.query implementation
fd -i 'queryClient.ts' packages/query-core/src --exec sed -n '1,260p' {}

# 2) Locate enabled / skipToken handling in implementation
rg -n -C3 '\benabled\b|skipToken|query\(' packages/query-core/src/queryClient.ts

# 3) Cross-check behavior in tests
rg -n -C3 'enabled.*false|skipToken|query\(' packages/query-core/src/__tests__/queryClient.test*

Expected result: you should find explicit handling where disabled execution does not fetch without usable cached data.

Also applies to: 125-130, 140-140

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/svelte/ssr.md` at line 10, Docs incorrectly state
queryClient.query() is not disabled by using defaultOptions.queries.enabled =
browser; update the text to clarify that setting defaultOptions.queries.enabled
= browser will gate QueryClient.query() on the server (it will not run and can
reject/silent-fail when .catch(noop) is used), so callers must explicitly
override enabled: true for server-run queries or use alternative hydration
patterns; reference QueryClient.query, defaultOptions.queries.enabled, browser
and the .catch(noop) snippet and correct the guidance around lines mentioned
(including the note at line 10 and related lines ~125-130 and ~140) to ensure
the doc warns that cache guarantees depend on enabling queries or using proper
SSR hydration.
docs/framework/react/guides/ssr.md (3)

231-236: ⚠️ Potential issue | 🟡 Minor

Define noop in code examples.

The example uses .catch(noop) but noop is never defined or imported, making the example non-runnable when copied.

✏️ Suggested fix
 export async function getStaticProps() {
   const queryClient = new QueryClient()
+  const noop = () => {}

   await queryClient
     .query({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/ssr.md` around lines 231 - 236, The example uses
.catch(noop) but noop is not defined; define a noop function or replace the
reference with an inline empty handler so the snippet is runnable. Locate the
example around queryClient.query({ queryKey: ['posts'], queryFn: getPosts }) and
either add a small noop declaration (e.g., const noop = () => {}) above it or
change .catch(noop) to .catch(() => {}) and ensure any references to noop are
updated accordingly.

315-320: ⚠️ Potential issue | 🟡 Minor

Define noop in Remix example as well.

Same issue as the Next.js example - noop needs to be defined for the example to be complete.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/ssr.md` around lines 315 - 320, The example uses
noop in the queryClient.query(...).catch(noop) call but never defines it; add a
simple no-op function (e.g., const noop = () => {}; or export/import an existing
noop) to the Remix example so the snippet is self-contained, placing the
definition near the example or at the top of the code block; ensure references
to queryClient.query and getPosts continue to work with the newly defined noop.

431-435: ⚠️ Potential issue | 🟡 Minor

Fix undefined userId variable.

Line 433 references userId which is never defined. The condition on line 431 checks user?.userId, so use that property directly.

✏️ Suggested fix
   if (user?.userId) {
     await queryClient.query({
-      queryKey: ['projects', userId],
+      queryKey: ['projects', user.userId],
       queryFn: getProjectsByUser,
     })
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/ssr.md` around lines 431 - 435, The code
references an undefined variable userId inside the queryClient.query call;
replace it with the checked property user.userId so the queryKey uses the actual
value. Update the block around the user check (the user?.userId condition) so
queryClient.query({ queryKey: ['projects', user.userId], queryFn:
getProjectsByUser }) uses user.userId instead of the undefined userId.
docs/framework/react/guides/advanced-ssr.md (1)

448-453: ⚠️ Potential issue | 🟡 Minor

These streaming snippets still use an undefined noop.

If .catch(noop) stays here, the sample needs to define or import noop; otherwise it is not copy-pasteable. Using .catch(() => {}) inline would avoid the extra symbol entirely.

Also applies to: 517-522

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/advanced-ssr.md` around lines 448 - 453, The
examples call queryClient.query(...).catch(noop) but noop is not defined or
imported, making the snippets non-copy-pasteable; either add a local noop
definition/import or replace the use with an inline empty handler (e.g.,
.catch(() => {})) in the queryClient.query calls (the occurrences of
queryClient.query({ queryKey: ['posts'], queryFn: getPosts }).catch(noop) and
the duplicate occurrence later) so the examples compile without adding an
external symbol.
packages/vue-query/src/queryClient.ts (1)

527-529: ⚠️ Potential issue | 🟡 Minor

Fix typo and misleading deprecation hint.

The deprecation message has a typo ("instead" missing space) and suggests void queryClient.infiniteQuery(options) which doesn't swallow rejections. This was flagged in a past review.

📝 Suggested fix for both occurrences
-   * `@deprecated` use void queryClient.infiniteQuery(options)instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version.
+   * `@deprecated` Use queryClient.infiniteQuery(options) instead. You can swallow errors with `.catch(noop)`. This method will be removed in the next major version.

Also applies to: 545-547

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-query/src/queryClient.ts` around lines 527 - 529, Fix the typo
and correct the deprecation hint in the JSDoc: add the missing space after
"options)" and replace the misleading suggestion "void
queryClient.infiniteQuery(options)" with a correct pattern that explicitly shows
swallowing errors, e.g. "queryClient.infiniteQuery(options).catch(noop)". Update
both JSDoc occurrences that reference queryClient.infiniteQuery (the deprecation
comment at the top of the deprecated method and the second occurrence mentioned)
so they read something like "use queryClient.infiniteQuery(options).catch(noop).
This method will be removed in the next major version."
🧹 Nitpick comments (2)
docs/framework/angular/guides/query-options.md (1)

41-41: Clarify that .catch(noop) is for fire-and-forget usage only.

A short note here would prevent readers from assuming swallowed errors are generally recommended.

📝 Suggested doc tweak
-queryClient.query(this.queries.post(23)).catch(noop)
+// Fire-and-forget preloading: suppress unhandled rejection warnings.
+// If you need error handling, `await queryClient.query(...)` instead.
+queryClient.query(this.queries.post(23)).catch(noop)
🤖 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, Add a short
clarifying note near the example using
queryClient.query(this.queries.post(23)).catch(noop) that explains .catch(noop)
is intended only for fire-and-forget usage and will swallow errors; instruct
readers to handle errors in production (e.g., await the promise with try/catch,
attach a proper error handler, or log the error) instead of using noop.
Reference the example call (queryClient.query and .catch(noop)) so the note
appears immediately adjacent to that snippet.
packages/query-core/src/queryClient.ts (1)

467-474: The as any cast bypasses type safety.

The as any cast when calling this.query(options as any) loses type safety between InfiniteQueryExecuteOptions and QueryExecuteOptions. This was noted in a past review comment.

While the runtime behavior is correct (since the behavior is injected via options.behavior), consider adding a brief comment explaining why the cast is safe, or creating an internal overload that accepts the behavior-augmented options type.

💡 Optional: Add explanatory comment
     options.behavior = infiniteQueryBehavior<
       TQueryFnData,
       TError,
       TQueryFnData,
       TPageParam
     >(options.pages)
+    // Safe cast: behavior property handles infinite query semantics
     return this.query(options as any)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-core/src/queryClient.ts` around lines 467 - 474, The cast
`options as any` in the call to this.query hides type mismatch between
InfiniteQueryExecuteOptions and QueryExecuteOptions; replace the unsafe cast by
adding a typed internal overload (e.g., private queryWithBehavior(options:
InfiniteQueryExecuteOptions<...>) that sets options.behavior =
infiniteQueryBehavior(...) and delegates to this.query) or, if you prefer
minimal change, add a concise explanatory comment directly above the call to
this.query(options as any) stating why the cast is safe (behavior injected by
infiniteQueryBehavior ensures runtime compatibility) and reference the involved
symbols (infiniteQueryBehavior, InfiniteQueryExecuteOptions,
QueryExecuteOptions, this.query, options.behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/framework/react/guides/advanced-ssr.md`:
- Around line 119-124: The examples call queryClient.query({ queryKey:
['posts'], queryFn: getPosts }) and then chain .catch(noop), which silences
awaited SSR errors; remove the .catch(noop) and let the promise rejection
surface (or replace with explicit error handling/logging) so server-side
prefetch failures are not swallowed; apply the same fix to the other occurrences
of queryClient.query(...).catch(noop) mentioned in the comment (lines 177-182,
244-249, 270-275) and ensure usage of queryClient.query remains the
manual-error-handling path.

In `@docs/framework/react/guides/prefetching.md`:
- Around line 68-78: The prefetchProjects function uses await but is not
declared async; change the function signature for prefetchProjects to be async
(or else remove await and return the promise), then await
queryClient.infiniteQuery(...) (which calls fetchProjects) and keep the
.catch(noop) for error suppression; locate the call site in prefetchProjects and
update the function declaration so the await is valid.
- Around line 48-56: The function prefetchTodos uses await but is not declared
async, causing a syntax error; fix by either marking prefetchTodos as async (so
you can await queryClient.query(...).catch(noop)) or remove the await and
return/fire-and-forget the promise from queryClient.query(...).catch(noop);
locate the prefetchTodos function and update its signature or the await usage
accordingly (referencing prefetchTodos and queryClient.query).

In `@docs/framework/react/typescript.md`:
- Line 216: The fire-and-forget call to queryClient.query(groupOptions()) can
produce unhandled rejections; update the call site (the invocation of
queryClient.query with groupOptions) to attach error handling — e.g., make it a
voided promise and add .catch(...) or otherwise await it inside a try/catch so
any rejection is logged/handled; ensure you reference the same groupOptions()
argument and keep the call as a non-blocking invocation if intended.

In `@docs/framework/solid/guides/prefetching.md`:
- Around line 97-102: The example uses .catch(noop) but never defines or imports
noop; update the example around queryClient.query (the call with queryKey
['article-comments', id] and queryFn getArticleCommentsById) to either add a
small noop definition (e.g., const noop = () => {}) above the snippet or import
noop from the appropriate library (e.g., import { noop } from
'@tanstack/query-core'), so the copied example is runnable and references the
defined/imported noop.

In `@docs/reference/QueryClient.md`:
- Line 116: The sentence describing options for infiniteQuery has a stray
trailing comma after the useInfiniteQuery link; open the line referencing
`infiniteQuery`, `query`, and the `useInfiniteQuery` link in QueryClient.md and
remove the trailing comma after the `useInfiniteQuery` link so the sentence ends
cleanly without the extra punctuation.
- Line 66: Update the sentence describing the query method to fix subject-verb
agreement: change "It will either resolves with the data or throw with an
error." to "It will either resolve with the data or throw an error." Ensure this
change is applied to the documentation line that references the async method
`query`.

In `@packages/query-core/src/__tests__/queryClient.test.tsx`:
- Around line 1003-1103: Add tests that set a cached null value via
queryClient.setQueryData(key, null) and assert queryClient.query treats null as
cached (not a miss) for the three branches: when enabled: false, when enabled:
() => false, and when queryFn: skipToken; use the same test helpers as existing
cases (queryKey(), skipToken, select callbacks) to verify query() returns the
selected/transformed result from the null cache where appropriate and does not
call the real queryFn or throw.

---

Outside diff comments:
In `@docs/framework/vue/guides/ssr.md`:
- Around line 58-65: The snippet calls await suspense() but no longer
destructures suspense from useQuery; fix by either restoring suspense in the
useQuery destructure (e.g., const { data, suspense } = useQuery(...)) or replace
the onServerPrefetch body to use the imperative API via the query client (e.g.,
call await queryClient.query(...) for the same queryKey/queryFn) so
onServerPrefetch no longer references an undefined suspense; update the example
accordingly using the symbols useQuery, suspense, onServerPrefetch, and
queryClient.query to locate the code to change.

---

Duplicate comments:
In `@docs/framework/react/guides/advanced-ssr.md`:
- Around line 448-453: The examples call queryClient.query(...).catch(noop) but
noop is not defined or imported, making the snippets non-copy-pasteable; either
add a local noop definition/import or replace the use with an inline empty
handler (e.g., .catch(() => {})) in the queryClient.query calls (the occurrences
of queryClient.query({ queryKey: ['posts'], queryFn: getPosts }).catch(noop) and
the duplicate occurrence later) so the examples compile without adding an
external symbol.

In `@docs/framework/react/guides/migrating-to-v5.md`:
- Line 57: Replace the unresolved placeholder token INSERT_FUTURE_V5_MINOR in
the sentence "These methods are deprecated as of Tanstack Query
`INSERT_FUTURE_V5_MINOR` and will be removed in v6." with a concrete version
identifier or neutral wording; locate the string INSERT_FUTURE_V5_MINOR in the
migrating-to-v5.md content and either substitute it with the actual release
version (e.g., `v5.x.y`) or reword to something like "a future v5 minor release"
so the documentation no longer contains the placeholder.

In `@docs/framework/react/guides/query-options.md`:
- Line 28: The example calling queryClient.query(groupOptions(23)) is a
fire-and-forget promise that can reject; update the example to handle rejections
by either awaiting the call inside an async function with try/catch or by
attaching a .catch handler to the returned promise (i.e., ensure
queryClient.query(...) is awaited inside an async function or followed by
.catch(err => /* log/handle */)), referencing the same queryClient.query and
groupOptions symbols so the example demonstrates safe error handling.

In `@docs/framework/react/guides/ssr.md`:
- Around line 231-236: The example uses .catch(noop) but noop is not defined;
define a noop function or replace the reference with an inline empty handler so
the snippet is runnable. Locate the example around queryClient.query({ queryKey:
['posts'], queryFn: getPosts }) and either add a small noop declaration (e.g.,
const noop = () => {}) above it or change .catch(noop) to .catch(() => {}) and
ensure any references to noop are updated accordingly.
- Around line 315-320: The example uses noop in the
queryClient.query(...).catch(noop) call but never defines it; add a simple no-op
function (e.g., const noop = () => {}; or export/import an existing noop) to the
Remix example so the snippet is self-contained, placing the definition near the
example or at the top of the code block; ensure references to queryClient.query
and getPosts continue to work with the newly defined noop.
- Around line 431-435: The code references an undefined variable userId inside
the queryClient.query call; replace it with the checked property user.userId so
the queryKey uses the actual value. Update the block around the user check (the
user?.userId condition) so queryClient.query({ queryKey: ['projects',
user.userId], queryFn: getProjectsByUser }) uses user.userId instead of the
undefined userId.

In `@docs/framework/solid/guides/query-options.md`:
- Line 35: The example calls queryClient.query(groupOptions(23)) without
handling rejections; update the example to handle promise rejection from
queryClient.query (or use await inside an async context) by adding proper error
handling (e.g., awaiting the call or attaching .catch) around the
queryClient.query invocation so failures from groupOptions(23) or
queryClient.query are not left as unhandled promise rejections; locate the
invocation of queryClient.query and change it to an awaited call or add a .catch
handler.

In `@docs/framework/solid/typescript.md`:
- Line 190: The example call to queryClient.query(groupOptions()) is a
fire-and-forget Promise and must handle rejections; update the snippet to either
await the call inside an async function or attach a .catch handler to
queryClient.query(...) to handle and log errors (reference: queryClient.query
and groupOptions) so the example doesn't produce unhandled promise rejections.

In `@docs/framework/svelte/ssr.md`:
- Line 10: Docs incorrectly state queryClient.query() is not disabled by using
defaultOptions.queries.enabled = browser; update the text to clarify that
setting defaultOptions.queries.enabled = browser will gate QueryClient.query()
on the server (it will not run and can reject/silent-fail when .catch(noop) is
used), so callers must explicitly override enabled: true for server-run queries
or use alternative hydration patterns; reference QueryClient.query,
defaultOptions.queries.enabled, browser and the .catch(noop) snippet and correct
the guidance around lines mentioned (including the note at line 10 and related
lines ~125-130 and ~140) to ensure the doc warns that cache guarantees depend on
enabling queries or using proper SSR hydration.

In `@packages/vue-query/src/queryClient.ts`:
- Around line 527-529: Fix the typo and correct the deprecation hint in the
JSDoc: add the missing space after "options)" and replace the misleading
suggestion "void queryClient.infiniteQuery(options)" with a correct pattern that
explicitly shows swallowing errors, e.g.
"queryClient.infiniteQuery(options).catch(noop)". Update both JSDoc occurrences
that reference queryClient.infiniteQuery (the deprecation comment at the top of
the deprecated method and the second occurrence mentioned) so they read
something like "use queryClient.infiniteQuery(options).catch(noop). This method
will be removed in the next major version."

---

Nitpick comments:
In `@docs/framework/angular/guides/query-options.md`:
- Line 41: Add a short clarifying note near the example using
queryClient.query(this.queries.post(23)).catch(noop) that explains .catch(noop)
is intended only for fire-and-forget usage and will swallow errors; instruct
readers to handle errors in production (e.g., await the promise with try/catch,
attach a proper error handler, or log the error) instead of using noop.
Reference the example call (queryClient.query and .catch(noop)) so the note
appears immediately adjacent to that snippet.

In `@packages/query-core/src/queryClient.ts`:
- Around line 467-474: The cast `options as any` in the call to this.query hides
type mismatch between InfiniteQueryExecuteOptions and QueryExecuteOptions;
replace the unsafe cast by adding a typed internal overload (e.g., private
queryWithBehavior(options: InfiniteQueryExecuteOptions<...>) that sets
options.behavior = infiniteQueryBehavior(...) and delegates to this.query) or,
if you prefer minimal change, add a concise explanatory comment directly above
the call to this.query(options as any) stating why the cast is safe (behavior
injected by infiniteQueryBehavior ensures runtime compatibility) and reference
the involved symbols (infiniteQueryBehavior, InfiniteQueryExecuteOptions,
QueryExecuteOptions, this.query, options.behavior).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7215ccec-7133-453e-bee6-4473b3ddd4cf

📥 Commits

Reviewing files that changed from the base of the PR and between ef62f7b and 531d3fd.

📒 Files selected for processing (58)
  • .changeset/famous-owls-battle.md
  • docs/eslint/stable-query-client.md
  • docs/framework/angular/guides/paginated-queries.md
  • docs/framework/angular/guides/query-options.md
  • docs/framework/angular/typescript.md
  • docs/framework/preact/reference/functions/usePrefetchQuery.md
  • docs/framework/preact/reference/index.md
  • docs/framework/preact/reference/interfaces/UsePrefetchQueryOptions.md
  • docs/framework/react/guides/advanced-ssr.md
  • docs/framework/react/guides/initial-query-data.md
  • docs/framework/react/guides/migrating-to-v5.md
  • docs/framework/react/guides/prefetching.md
  • docs/framework/react/guides/query-options.md
  • docs/framework/react/guides/ssr.md
  • docs/framework/react/reference/infiniteQueryOptions.md
  • docs/framework/react/reference/queryOptions.md
  • docs/framework/react/reference/usePrefetchInfiniteQuery.md
  • docs/framework/react/reference/usePrefetchQuery.md
  • docs/framework/react/typescript.md
  • docs/framework/solid/guides/prefetching.md
  • docs/framework/solid/guides/query-options.md
  • docs/framework/solid/typescript.md
  • docs/framework/svelte/ssr.md
  • docs/framework/vue/guides/prefetching.md
  • docs/framework/vue/guides/ssr.md
  • docs/reference/QueryClient.md
  • packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts
  • packages/angular-query-experimental/src/__tests__/query-options.test-d.ts
  • packages/preact-query/src/__tests__/infiniteQueryOptions.test-d.tsx
  • packages/preact-query/src/__tests__/queryOptions.test-d.tsx
  • packages/preact-query/src/__tests__/useInfiniteQuery.test-d.tsx
  • packages/preact-query/src/__tests__/usePrefetchInfiniteQuery.test-d.tsx
  • packages/preact-query/src/__tests__/usePrefetchQuery.test-d.tsx
  • packages/preact-query/src/types.ts
  • packages/preact-query/src/usePrefetchInfiniteQuery.tsx
  • packages/preact-query/src/usePrefetchQuery.tsx
  • packages/query-core/src/__tests__/queryClient.test-d.tsx
  • packages/query-core/src/__tests__/queryClient.test.tsx
  • packages/query-core/src/queryClient.ts
  • packages/query-core/src/types.ts
  • packages/react-query/src/__tests__/infiniteQueryOptions.test-d.tsx
  • packages/react-query/src/__tests__/queryOptions.test-d.tsx
  • packages/react-query/src/__tests__/useInfiniteQuery.test-d.tsx
  • packages/react-query/src/__tests__/usePrefetchInfiniteQuery.test-d.tsx
  • packages/react-query/src/__tests__/usePrefetchQuery.test-d.tsx
  • packages/react-query/src/__tests__/useQuery.promise.test.tsx
  • packages/react-query/src/types.ts
  • packages/react-query/src/usePrefetchInfiniteQuery.tsx
  • packages/react-query/src/usePrefetchQuery.tsx
  • packages/solid-query/src/__tests__/infiniteQueryOptions.test-d.tsx
  • packages/solid-query/src/__tests__/queryOptions.test-d.tsx
  • packages/svelte-query/tests/infiniteQueryOptions.test-d.ts
  • packages/svelte-query/tests/queryOptions.test-d.ts
  • packages/vue-query/src/__tests__/infiniteQueryOptions.test-d.ts
  • packages/vue-query/src/__tests__/queryClient.test-d.ts
  • packages/vue-query/src/__tests__/queryClient.test.ts
  • packages/vue-query/src/__tests__/queryOptions.test-d.ts
  • packages/vue-query/src/queryClient.ts
💤 Files with no reviewable changes (4)
  • docs/framework/preact/reference/index.md
  • docs/framework/preact/reference/interfaces/UsePrefetchQueryOptions.md
  • packages/preact-query/src/types.ts
  • packages/react-query/src/types.ts

Comment on lines +1003 to +1103
test('should return cached data when disabled and apply select', async () => {
const key = queryKey()
const queryFn = vi.fn(() => Promise.resolve('fetched-data'))

await queryClient.prefetchInfiniteQuery({
queryClient.setQueryData(key, 'cached-data')

const result = await queryClient.query({
queryKey: key,
queryFn: ({ pageParam }) => Number(pageParam),
initialPageParam: 10,
queryFn,
enabled: false,
staleTime: 0,
select: (data) => `${data}-selected`,
})

const result = queryClient.getQueryData(key)

expect(result).toEqual({
pages: [10],
pageParams: [10],
})
expect(result).toBe('cached-data-selected')
expect(queryFn).not.toHaveBeenCalled()
})

test('should prefetch multiple pages', async () => {
test('should throw when skipToken is provided and no cached data exists', async () => {
const key = queryKey()
const select = vi.fn((data: unknown) => (data as string).length)

await queryClient.prefetchInfiniteQuery({
queryKey: key,
queryFn: ({ pageParam }) => String(pageParam),
getNextPageParam: (_lastPage, _pages, lastPageParam) =>
lastPageParam + 5,
initialPageParam: 10,
pages: 3,
})

const result = queryClient.getQueryData(key)
await expect(
queryClient.query({
queryKey: key,
queryFn: skipToken,
select,
}),
).rejects.toThrowError()

expect(result).toEqual({
pages: ['10', '15', '20'],
pageParams: [10, 15, 20],
})
expect(select).not.toHaveBeenCalled()
})

test('should stop prefetching if getNextPageParam returns undefined', async () => {
test('should return cached data when skipToken is provided', async () => {
const key = queryKey()
let count = 0

await queryClient.prefetchInfiniteQuery({
queryClient.setQueryData(key, 'cached-data')

const result = await queryClient.query({
queryKey: key,
queryFn: ({ pageParam }) => String(pageParam),
getNextPageParam: (_lastPage, _pages, lastPageParam) => {
count++
return lastPageParam >= 20 ? undefined : lastPageParam + 5
},
initialPageParam: 10,
pages: 5,
queryFn: skipToken,
select: (data: unknown) => (data as string).length,
})

const result = queryClient.getQueryData(key)
expect(result).toBe('cached-data'.length)
})

expect(result).toEqual({
pages: ['10', '15', '20'],
pageParams: [10, 15, 20],
test('should return cached data when skipToken and enabled false are both provided', async () => {
const key = queryKey()

queryClient.setQueryData(key, { value: 'cached-data' })

const result = await queryClient.query({
queryKey: key,
queryFn: skipToken,
enabled: false,
select: (data: { value: string }) => data.value.toUpperCase(),
})

// this check ensures we're exiting the fetch loop early
expect(count).toBe(3)
expect(result).toBe('CACHED-DATA')
})
})

describe('prefetchQuery', () => {
test('should not type-error with strict query key', async () => {
type StrictData = 'data'
type StrictQueryKey = ['strict', ...ReturnType<typeof queryKey>]
const key: StrictQueryKey = ['strict', ...queryKey()]

const fetchFn: QueryFunction<StrictData, StrictQueryKey> = () =>
Promise.resolve('data')
test('should throw when enabled resolves true and skipToken are provided with no cached data', async () => {
await expect(
queryClient.query({
queryKey: queryKey(),
queryFn: skipToken,
enabled: true,
}),
).rejects.toThrowError()
})

await queryClient.prefetchQuery<
StrictData,
any,
StrictData,
StrictQueryKey
>({ queryKey: key, queryFn: fetchFn })
test('should return cached data when enabled resolves false and skipToken are provided', async () => {
const key1 = queryKey()
queryClient.setQueryData(key1, { value: 'cached-data' })

const result = queryClient.getQueryData(key)
const booleanDisabledResult = await queryClient.query({
queryKey: key1,
queryFn: skipToken,
enabled: false,
select: (data: { value: string }) => data.value.length,
})

expect(result).toEqual('data')
expect(booleanDisabledResult).toBe('cached-data'.length)
})

test('should return undefined when an error is thrown', async () => {
test('should return cached data when enabled callback returns false even if queryFn would return different data', async () => {
const key = queryKey()
const queryFn = vi.fn(() => Promise.resolve('fetched-data'))

queryClient.setQueryData(key, 'cached-data')

const result = await queryClient.query({
queryKey: key,
queryFn,
enabled: () => false,
})

expect(result).toBe('cached-data')
expect(queryFn).not.toHaveBeenCalled()
})
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

Add a null cache regression case for the new disabled/skipToken branches.

All of the new “cached data exists” assertions here use truthy cached values, so the data == null bug still slips through this suite. Please cover setQueryData(key, null) for enabled: false, enabled: () => false, and queryFn: skipToken so query() proves it only treats undefined as a cache miss.

Suggested regression cases
+    test('should return cached null when disabled', async () => {
+      const key = queryKey()
+      queryClient.setQueryData(key, null)
+
+      await expect(
+        queryClient.query({
+          queryKey: key,
+          queryFn: vi.fn(() => Promise.resolve('fetched-data')),
+          enabled: false,
+        }),
+      ).resolves.toBeNull()
+    })
+
+    test('should return cached null when skipToken is provided', async () => {
+      const key = queryKey()
+      queryClient.setQueryData(key, null)
+
+      await expect(
+        queryClient.query({
+          queryKey: key,
+          queryFn: skipToken,
+        }),
+      ).resolves.toBeNull()
+    })
📝 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
test('should return cached data when disabled and apply select', async () => {
const key = queryKey()
const queryFn = vi.fn(() => Promise.resolve('fetched-data'))
await queryClient.prefetchInfiniteQuery({
queryClient.setQueryData(key, 'cached-data')
const result = await queryClient.query({
queryKey: key,
queryFn: ({ pageParam }) => Number(pageParam),
initialPageParam: 10,
queryFn,
enabled: false,
staleTime: 0,
select: (data) => `${data}-selected`,
})
const result = queryClient.getQueryData(key)
expect(result).toEqual({
pages: [10],
pageParams: [10],
})
expect(result).toBe('cached-data-selected')
expect(queryFn).not.toHaveBeenCalled()
})
test('should prefetch multiple pages', async () => {
test('should throw when skipToken is provided and no cached data exists', async () => {
const key = queryKey()
const select = vi.fn((data: unknown) => (data as string).length)
await queryClient.prefetchInfiniteQuery({
queryKey: key,
queryFn: ({ pageParam }) => String(pageParam),
getNextPageParam: (_lastPage, _pages, lastPageParam) =>
lastPageParam + 5,
initialPageParam: 10,
pages: 3,
})
const result = queryClient.getQueryData(key)
await expect(
queryClient.query({
queryKey: key,
queryFn: skipToken,
select,
}),
).rejects.toThrowError()
expect(result).toEqual({
pages: ['10', '15', '20'],
pageParams: [10, 15, 20],
})
expect(select).not.toHaveBeenCalled()
})
test('should stop prefetching if getNextPageParam returns undefined', async () => {
test('should return cached data when skipToken is provided', async () => {
const key = queryKey()
let count = 0
await queryClient.prefetchInfiniteQuery({
queryClient.setQueryData(key, 'cached-data')
const result = await queryClient.query({
queryKey: key,
queryFn: ({ pageParam }) => String(pageParam),
getNextPageParam: (_lastPage, _pages, lastPageParam) => {
count++
return lastPageParam >= 20 ? undefined : lastPageParam + 5
},
initialPageParam: 10,
pages: 5,
queryFn: skipToken,
select: (data: unknown) => (data as string).length,
})
const result = queryClient.getQueryData(key)
expect(result).toBe('cached-data'.length)
})
expect(result).toEqual({
pages: ['10', '15', '20'],
pageParams: [10, 15, 20],
test('should return cached data when skipToken and enabled false are both provided', async () => {
const key = queryKey()
queryClient.setQueryData(key, { value: 'cached-data' })
const result = await queryClient.query({
queryKey: key,
queryFn: skipToken,
enabled: false,
select: (data: { value: string }) => data.value.toUpperCase(),
})
// this check ensures we're exiting the fetch loop early
expect(count).toBe(3)
expect(result).toBe('CACHED-DATA')
})
})
describe('prefetchQuery', () => {
test('should not type-error with strict query key', async () => {
type StrictData = 'data'
type StrictQueryKey = ['strict', ...ReturnType<typeof queryKey>]
const key: StrictQueryKey = ['strict', ...queryKey()]
const fetchFn: QueryFunction<StrictData, StrictQueryKey> = () =>
Promise.resolve('data')
test('should throw when enabled resolves true and skipToken are provided with no cached data', async () => {
await expect(
queryClient.query({
queryKey: queryKey(),
queryFn: skipToken,
enabled: true,
}),
).rejects.toThrowError()
})
await queryClient.prefetchQuery<
StrictData,
any,
StrictData,
StrictQueryKey
>({ queryKey: key, queryFn: fetchFn })
test('should return cached data when enabled resolves false and skipToken are provided', async () => {
const key1 = queryKey()
queryClient.setQueryData(key1, { value: 'cached-data' })
const result = queryClient.getQueryData(key)
const booleanDisabledResult = await queryClient.query({
queryKey: key1,
queryFn: skipToken,
enabled: false,
select: (data: { value: string }) => data.value.length,
})
expect(result).toEqual('data')
expect(booleanDisabledResult).toBe('cached-data'.length)
})
test('should return undefined when an error is thrown', async () => {
test('should return cached data when enabled callback returns false even if queryFn would return different data', async () => {
const key = queryKey()
const queryFn = vi.fn(() => Promise.resolve('fetched-data'))
queryClient.setQueryData(key, 'cached-data')
const result = await queryClient.query({
queryKey: key,
queryFn,
enabled: () => false,
})
expect(result).toBe('cached-data')
expect(queryFn).not.toHaveBeenCalled()
})
test('should return cached data when disabled and apply select', async () => {
const key = queryKey()
const queryFn = vi.fn(() => Promise.resolve('fetched-data'))
queryClient.setQueryData(key, 'cached-data')
const result = await queryClient.query({
queryKey: key,
queryFn,
enabled: false,
staleTime: 0,
select: (data) => `${data}-selected`,
})
expect(result).toBe('cached-data-selected')
expect(queryFn).not.toHaveBeenCalled()
})
test('should throw when skipToken is provided and no cached data exists', async () => {
const key = queryKey()
const select = vi.fn((data: unknown) => (data as string).length)
await expect(
queryClient.query({
queryKey: key,
queryFn: skipToken,
select,
}),
).rejects.toThrowError()
expect(select).not.toHaveBeenCalled()
})
test('should return cached data when skipToken is provided', async () => {
const key = queryKey()
queryClient.setQueryData(key, 'cached-data')
const result = await queryClient.query({
queryKey: key,
queryFn: skipToken,
select: (data: unknown) => (data as string).length,
})
expect(result).toBe('cached-data'.length)
})
test('should return cached data when skipToken and enabled false are both provided', async () => {
const key = queryKey()
queryClient.setQueryData(key, { value: 'cached-data' })
const result = await queryClient.query({
queryKey: key,
queryFn: skipToken,
enabled: false,
select: (data: { value: string }) => data.value.toUpperCase(),
})
expect(result).toBe('CACHED-DATA')
})
test('should throw when enabled resolves true and skipToken are provided with no cached data', async () => {
await expect(
queryClient.query({
queryKey: queryKey(),
queryFn: skipToken,
enabled: true,
}),
).rejects.toThrowError()
})
test('should return cached data when enabled resolves false and skipToken are provided', async () => {
const key1 = queryKey()
queryClient.setQueryData(key1, { value: 'cached-data' })
const booleanDisabledResult = await queryClient.query({
queryKey: key1,
queryFn: skipToken,
enabled: false,
select: (data: { value: string }) => data.value.length,
})
expect(booleanDisabledResult).toBe('cached-data'.length)
})
test('should return cached data when enabled callback returns false even if queryFn would return different data', async () => {
const key = queryKey()
const queryFn = vi.fn(() => Promise.resolve('fetched-data'))
queryClient.setQueryData(key, 'cached-data')
const result = await queryClient.query({
queryKey: key,
queryFn,
enabled: () => false,
})
expect(result).toBe('cached-data')
expect(queryFn).not.toHaveBeenCalled()
})
test('should return cached null when disabled', async () => {
const key = queryKey()
queryClient.setQueryData(key, null)
await expect(
queryClient.query({
queryKey: key,
queryFn: vi.fn(() => Promise.resolve('fetched-data')),
enabled: false,
}),
).resolves.toBeNull()
})
test('should return cached null when skipToken is provided', async () => {
const key = queryKey()
queryClient.setQueryData(key, null)
await expect(
queryClient.query({
queryKey: key,
queryFn: skipToken,
}),
).resolves.toBeNull()
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-core/src/__tests__/queryClient.test.tsx` around lines 1003 -
1103, Add tests that set a cached null value via queryClient.setQueryData(key,
null) and assert queryClient.query treats null as cached (not a miss) for the
three branches: when enabled: false, when enabled: () => false, and when
queryFn: skipToken; use the same test helpers as existing cases (queryKey(),
skipToken, select callbacks) to verify query() returns the selected/transformed
result from the null cache where appropriate and does not call the real queryFn
or throw.

DogPawHat and others added 4 commits April 8, 2026 17:28
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@DogPawHat
Copy link
Copy Markdown
Contributor Author

@TkDodo btw let me know if I need to start a new PR, this has a lot of commits and outdated Coderabbit comments now sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants