Skip to content

feat(query-core): add refetchIntervalOnServer option#10267

Open
K-Mistele wants to merge 1 commit intoTanStack:mainfrom
K-Mistele:feat/refetch-interval-on-server
Open

feat(query-core): add refetchIntervalOnServer option#10267
K-Mistele wants to merge 1 commit intoTanStack:mainfrom
K-Mistele:feat/refetch-interval-on-server

Conversation

@K-Mistele
Copy link

@K-Mistele K-Mistele commented Mar 13, 2026

Summary

  • Adds a new refetchIntervalOnServer option to QueryObserverOptions that allows refetchInterval to remain active in server environments
  • Defaults to false, preserving existing behavior where refetchInterval is disabled when isServer is true
  • Includes two new tests validating both the default behavior and the opt-in behavior

Motivation

@tanstack/query-core defines isServer = typeof window === 'undefined' || 'Deno' in globalThis and uses it in QueryObserver#updateRefetchInterval to unconditionally disable refetchInterval on the server. This makes sense for SSR, where polling during server-side rendering is wasteful.

However, there are legitimate server-side use cases where polling is essential. For example, long-running daemon processes and background workers that use TanStack Query (often via TanStack DB query collections) for real-time data synchronization rely on refetchInterval as a polling-based fallback when primary sync channels (like WebSockets or Electric SQL) are unavailable.

Currently, the only workaround is to monkey-patch the isServer export (as the test utils already do via setIsServer), which is fragile and not a supported API.

API Design

This follows the exact same pattern as the existing refetchIntervalInBackground option:

const observer = new QueryObserver(queryClient, {
  queryKey: ['sync-data'],
  queryFn: fetchData,
  refetchInterval: 5000,
  refetchIntervalOnServer: true, // NEW: opt-in to server-side polling
})

It can also be set as a default for all queries via QueryClient:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchIntervalOnServer: true,
    },
  },
})

Changes

File Change
packages/query-core/src/types.ts Add refetchIntervalOnServer?: boolean to QueryObserverOptions with JSDoc
packages/query-core/src/queryObserver.ts Check this.options.refetchIntervalOnServer before skipping interval setup on server
packages/query-core/src/__tests__/queryObserver.test.tsx Add tests for default (no polling on server) and opt-in (polling on server) behaviors

Test plan

  • Existing test suite passes (496 tests, all passing)
  • TypeScript compilation passes
  • New test: should not refetch on server by default -- verifies default behavior is unchanged
  • New test: should refetch on server when refetchIntervalOnServer is true -- verifies opt-in works

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added refetchIntervalOnServer configuration option to control server-side query refetch behavior. By default, refetch intervals are now disabled on servers; enable this option to allow interval-based refetches in server environments.

Add a new `refetchIntervalOnServer` option to `QueryObserverOptions` that
allows `refetchInterval` to remain active in server environments.

By default, `refetchInterval` is disabled when `isServer` is true (i.e.
`typeof window === 'undefined'`), which makes sense for SSR scenarios.
However, long-running server processes like daemons and background workers
that use TanStack Query for data synchronization need polling to function.

This option follows the same pattern as `refetchIntervalInBackground` --
a per-query boolean that defaults to `false`, preserving existing behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 13, 2026

⚠️ No Changeset found

Latest commit: af4cc96

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

This PR introduces an opt-in mechanism for server-side refetch interval behavior by adding a new refetchIntervalOnServer option to query observer configurations. The changes include type definitions, core logic updates to gate refetch behavior on this flag, and test cases validating the behavior in both enabled and disabled scenarios.

Changes

Cohort / File(s) Summary
Type Definitions
packages/query-core/src/types.ts
Adds optional refetchIntervalOnServer?: boolean field to QueryObserverOptions and InfiniteQueryObserverOptions interfaces to enable opt-in server-side refetch interval behavior.
Core Logic
packages/query-core/src/queryObserver.ts
Modifies refetch interval and stale-time handling to gate server-side behavior on the new refetchIntervalOnServer option, making server-side polling opt-in rather than automatic.
Test Coverage
packages/query-core/src/__tests__/queryObserver.test.tsx
Adds test cases validating server-side fetch behavior with and without the refetchIntervalOnServer option enabled, using a server context helper and fetch invocation counter.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit whispers through the code,
"On servers now, we pause and wait,
Unless you say to refetch and load,
Control is ours—we set the gate!" ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers changes, motivation, and API design but is missing the required checklist section with checkboxes for contribution steps and testing. Complete the checklist section by checking the boxes indicating the contributing guide was followed and local testing with pnpm run test:pr was performed.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main feature added: a new refetchIntervalOnServer option for query-core.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can generate a title for your PR based on the changes with custom instructions.

Set the reviews.auto_title_instructions setting to generate a title for your PR based on the changes in the PR with custom instructions.

Copy link
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.

🧹 Nitpick comments (1)
packages/query-core/src/__tests__/queryObserver.test.tsx (1)

896-920: Consider using a more precise assertion for consistency.

The test correctly verifies that refetch intervals work on the server when refetchIntervalOnServer: true. However, line 916 uses toBeGreaterThan(1) which is less precise than similar tests in this file (e.g., lines 750-751 use exact toBe(2)).

With a 30ms wait and 10ms interval, the expected count should be 4 (1 initial + 3 interval fetches).

🔧 Optional: Use exact count for consistency
     // Should have the initial fetch plus refetches from the interval
-    expect(count).toBeGreaterThan(1)
+    expect(count).toBe(4)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query-core/src/__tests__/queryObserver.test.tsx` around lines 896 -
920, The assertion in the test for QueryObserver ('should refetch on server when
refetchIntervalOnServer is true') is imprecise: replace the loose
expect(count).toBeGreaterThan(1) with an exact expectation matching the
configured timings—given refetchInterval: 10 and awaiting
vi.advanceTimersByTimeAsync(30) (1 initial fetch + 3 interval fetches), assert
expect(count).toBe(4); update the assertion in the test that uses the count
variable and the observer/QueryObserver setup accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/query-core/src/__tests__/queryObserver.test.tsx`:
- Around line 896-920: The assertion in the test for QueryObserver ('should
refetch on server when refetchIntervalOnServer is true') is imprecise: replace
the loose expect(count).toBeGreaterThan(1) with an exact expectation matching
the configured timings—given refetchInterval: 10 and awaiting
vi.advanceTimersByTimeAsync(30) (1 initial fetch + 3 interval fetches), assert
expect(count).toBe(4); update the assertion in the test that uses the count
variable and the observer/QueryObserver setup accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ced1190-c84a-404e-96e3-d4fe6d2ea623

📥 Commits

Reviewing files that changed from the base of the PR and between 489359a and af4cc96.

📒 Files selected for processing (3)
  • packages/query-core/src/__tests__/queryObserver.test.tsx
  • packages/query-core/src/queryObserver.ts
  • packages/query-core/src/types.ts

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.

1 participant