You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The staged work has validated the intended boundary without adding a parallel Query API:
Persistence-safe on-demand metadata — #1644 keeps runtime subscription state out of dehydrated Query metadata and locks the contract with structuredClone(dehydrate(queryClient)) coverage.
Invalidation behavior matrix — #1655 characterizes eager/on-demand exact and prefix invalidation, overlapping subsets, failed refetch retention, inactive cached queries, and removed queries. TanStack Query remains the invalidation authority.
Option and projection contract — #1653, #1654, and #1665 document and extend the supported flat top-level observer options; preserve QueryClient.defaultOptions; and define adapter select as row extraction while Query retains the original response envelope.
Ownership groundwork — #1664 extracts the private row/query ownership operations without semantic change.
Runtime and business scoping guidance — #1667 and #1671 document plain factories and stable memoization by runtime QueryClient and business scope. No binding API was needed.
Cancellation and subset-cleanup coverage/fix — #1673 tracks temporary readiness listeners, releases them during unload/cleanup, and covers abort propagation, late results, cache removal, fast remount, and invalidation/resubscription.
Decisions made while executing the RFC
Keep the public configuration flat. A nested Query options bag would duplicate top-level fields and introduce precedence rules.
Prefer ordinary TypeScript factories over a .bind(...) runtime API unless real applications demonstrate that they are insufficient.
Keep relational subsets in LoadSubsetOptions; collection identity is only for runtime and business scope.
initialData and placeholderData are semantic adapter work, not mechanical option forwarding: materializing temporary document-cache values into normalized DB rows needs an explicit contract.
In progress
#1672 is the remaining ownership-map cleanup PR. It is open, not merged. It must resolve the requested simplification/cleanup review before landing; empty ownership entries encode an important resolved-empty baseline, so this is not a mechanical map deletion.
Write a dedicated design for initialData / placeholderData (including cache-vs-materialized-row authority, direct-write behavior, and lifecycle/expiry semantics).
Implement only the approved initialization semantics with focused tests and docs.
loadSubset deduplication, pagination mechanics, and a general lease manager belong to the separate loadSubset/pagination RFC, not this RFC.
We recently reviewed the open issues and related historical fixes in this repository to identify clusters of related work. One cluster that stood out is the integration boundary between @tanstack/query-db-collection, TanStack Query, and TanStack DB's normalized row store.
This RFC summarizes that cluster and proposes a staged sequence of small, non-breaking PRs. The goal is not to approve a large rewrite. The goal is to agree on the direction so each PR can improve one part of the boundary without making the adapter more complicated or duplicating more TanStack Query behavior.
Cluster summary
Active issues
#183 Deepen the integration with Query asks how much of TanStack Query's behavior should be supported, including refresh on mount/window focus and related options.
#345 Add select Option to query-db-collection covers wrapped API responses and extracting rows from response envelopes. Current main appears to support this, but it also exposes a larger projection question: this select extracts rows for DB materialization rather than behaving exactly like TanStack Query's select.
These closed items are not evidence that current main is broken. They are useful context because they show the kinds of integration problems that have recurred around this boundary:
The cluster is not just a list of missing flags. It points to one integration-boundary problem:
query-db-collection bridges TanStack Query's document-cache model and TanStack DB's normalized row-store model.
That bridge is valuable, but it becomes hard to maintain when the adapter manually shadows TanStack Query behavior. Option forwarding, invalidation, persistence, QueryClient scoping, parameterization, row projection, cancellation, and lifecycle management all become adapter responsibilities unless the ownership boundary is explicit.
TanStack DB owns row identity, materialization, local queries, optimistic transactions, and normalized row state.
The adapter owns projection between Query results and DB rows, plus the lease that tracks which query/subset currently owns which materialized rows.
This boundary keeps Query semantics in Query and DB semantics in DB. The adapter should connect the two, not become a partial reimplementation of TanStack Query.
Proposed PR sequence
PR 1: persistence regression coverage for on-demand collections
Issue #901 is specifically about Query persistence: an on-demand collection caused @tanstack/react-query-persist-client with an IndexedDB persister to fail with a DataCloneError because a function-bearing subscription object reached Query metadata.
The first PR should add regression coverage for that persistence path. The test should create an on-demand query collection, load a subset, dehydrate the QueryClient, and verify that the persisted/dehydrated payload is structured-clone safe. If the repository already has a lightweight IndexedDB persister test utility, use it. Otherwise, structuredClone(dehydrate(queryClient)) is the right focused unit-level guard because IndexedDB persistence fails at the structured clone step.
The test should also assert that adapter-owned Query metadata does not contain functions, subscriptions, collection instances, or other runtime-only values.
If current main already passes, this PR still locks in the persistence contract and narrows #901. If it fails, the fix should be minimal and non-breaking: keep ctx.meta.loadSubsetOptions working, but ensure the metadata copy stored in Query is persistence-safe plain data.
Before adding a collection-specific invalidation API, document and test the behavior matrix:
active query + exact invalidation;
active query + prefix invalidation;
inactive cached query;
on-demand subset query;
overlapping subset queries;
failed refetch after invalidation;
removed query;
persisted or retained query.
The likely public API can remain a thin convenience wrapper over queryClient.invalidateQueries, if one is needed at all. TanStack Query should remain the invalidation authority.
PR 3: Query option pass-through and compatibility table
Move away from adding individual Query-like flags to a flat adapter config one by one. Instead, introduce or design toward a Query options object/factory whose type is derived from TanStack Query's option types, excluding only fields the adapter must own.
The documentation should classify options as:
inherited unchanged from TanStack Query;
adapter-owned or reinterpreted;
unsupported, with rationale.
This should reduce bugs where an exposed Query option is forgotten, forwarded incorrectly, or accidentally overrides QueryClient.defaultOptions.
Current select support appears to solve row extraction from wrapped responses. The follow-up design question is naming and semantics: this adapter-level select is a row projection for DB materialization, not exactly TanStack Query's select.
A future additive API could make this more explicit, for example:
The optional reverse projection matters for direct writes and Query cache patching. Without a lawful reverse projection and subset-membership information, invalidating affected queries is safer than fabricating a wrapped cache response.
This PR should be additive and should not remove the existing select option.
This would avoid process-global QueryClient assumptions and support TanStack Start loaders, SSR isolation, tests with multiple QueryClients, auth/account switching, and deterministic disposal.
business scope: tenant, project, account, workspace, API endpoint, auth context;
relational subset scope: predicates, sorting, limits, offsets pushed down from a live query.
A collection-family API could make business scope first-class while keeping query-driven subset state explicit. This avoids forcing users to encode unrelated concepts into one anonymous meta bag or into ad hoc closures.
The adapter currently has to coordinate observers, unsubscribers, query-to-row ownership, row-to-query ownership, reference counts, retained query state, and retention timers. Those responsibilities would be easier to reason about if centered around a query/subset lease object.
A lease model should distinguish three lifetimes:
Query observer lifetime;
materialized row ownership lifetime;
Query cache lifetime.
It should also make cancellation and late network results generation-safe, so a stale result cannot mutate rows after cancellation, disposal, or recreation.
Non-goals
No immediate breaking changes.
No one-shot rewrite.
No competing invalidation system outside TanStack Query.
No removal of existing select behavior without migration.
No removal of existing meta behavior without migration.
No process-global QueryClient workaround for SSR/request-lifetime issues.
Questions for maintainers
Does this describe the right integration boundary between TanStack Query, TanStack DB, and query-db-collection?
Are there issues or PRs missing from this cluster summary?
Is the proposed PR sequence the right order?
Should the API work be strictly additive first, with deprecations considered later?
Are there SSR, hydration, persistence, or Start-specific constraints this roadmap should account for before implementation begins?
Background
Status — 2026-07-16
Completed, merged work
The staged work has validated the intended boundary without adding a parallel Query API:
structuredClone(dehydrate(queryClient))coverage.QueryClient.defaultOptions; and define adapterselectas row extraction while Query retains the original response envelope.QueryClientand business scope. No binding API was needed.Decisions made while executing the RFC
.bind(...)runtime API unless real applications demonstrate that they are insufficient.LoadSubsetOptions; collection identity is only for runtime and business scope.initialDataandplaceholderDataare semantic adapter work, not mechanical option forwarding: materializing temporary document-cache values into normalized DB rows needs an explicit contract.In progress
Remaining RFC work
initialData/placeholderData(including cache-vs-materialized-row authority, direct-write behavior, and lifecycle/expiry semantics).loadSubsetdeduplication, pagination mechanics, and a general lease manager belong to the separate loadSubset/pagination RFC, not this RFC.We recently reviewed the open issues and related historical fixes in this repository to identify clusters of related work. One cluster that stood out is the integration boundary between
@tanstack/query-db-collection, TanStack Query, and TanStack DB's normalized row store.This RFC summarizes that cluster and proposes a staged sequence of small, non-breaking PRs. The goal is not to approve a large rewrite. The goal is to agree on the direction so each PR can improve one part of the boundary without making the adapter more complicated or duplicating more TanStack Query behavior.
Cluster summary
Active issues
queryClient.invalidateQueriesand Query's matching semantics.selectextracts rows for DB materialization rather than behaving exactly like TanStack Query'sselect.metaappear partly addressed on current main, while cancellation and lifecycle behavior remain relevant.[QueryCollection] queryClient must be providedon TanStack Start's beforeLoad points to runtimeQueryClientscoping, especially for SSR and TanStack Start request lifetimes.Related historical fixes
These closed items are not evidence that current main is broken. They are useful context because they show the kinds of integration problems that have recurred around this boundary:
undefinedvalues suppressingQueryClient.defaultOptions.Diagnosis
The cluster is not just a list of missing flags. It points to one integration-boundary problem:
query-db-collectionbridges TanStack Query's document-cache model and TanStack DB's normalized row-store model.That bridge is valuable, but it becomes hard to maintain when the adapter manually shadows TanStack Query behavior. Option forwarding, invalidation, persistence, QueryClient scoping, parameterization, row projection, cancellation, and lifecycle management all become adapter responsibilities unless the ownership boundary is explicit.
A useful target boundary is:
This boundary keeps Query semantics in Query and DB semantics in DB. The adapter should connect the two, not become a partial reimplementation of TanStack Query.
Proposed PR sequence
PR 1: persistence regression coverage for on-demand collections
Related issue: #901
Issue #901 is specifically about Query persistence: an on-demand collection caused
@tanstack/react-query-persist-clientwith an IndexedDB persister to fail with aDataCloneErrorbecause a function-bearingsubscriptionobject reached Query metadata.The first PR should add regression coverage for that persistence path. The test should create an on-demand query collection, load a subset, dehydrate the QueryClient, and verify that the persisted/dehydrated payload is structured-clone safe. If the repository already has a lightweight IndexedDB persister test utility, use it. Otherwise,
structuredClone(dehydrate(queryClient))is the right focused unit-level guard because IndexedDB persistence fails at the structured clone step.The test should also assert that adapter-owned Query metadata does not contain functions, subscriptions, collection instances, or other runtime-only values.
If current main already passes, this PR still locks in the persistence contract and narrows #901. If it fails, the fix should be minimal and non-breaking: keep
ctx.meta.loadSubsetOptionsworking, but ensure the metadata copy stored in Query is persistence-safe plain data.PR 2: invalidation behavior matrix
Related issue: #344
Before adding a collection-specific invalidation API, document and test the behavior matrix:
The likely public API can remain a thin convenience wrapper over
queryClient.invalidateQueries, if one is needed at all. TanStack Query should remain the invalidation authority.PR 3: Query option pass-through and compatibility table
Related issues: #183, #346
Move away from adding individual Query-like flags to a flat adapter config one by one. Instead, introduce or design toward a Query options object/factory whose type is derived from TanStack Query's option types, excluding only fields the adapter must own.
The documentation should classify options as:
This should reduce bugs where an exposed Query option is forgotten, forwarded incorrectly, or accidentally overrides
QueryClient.defaultOptions.PR 4: clarify row projection semantics
Related issue: #345
Current
selectsupport appears to solve row extraction from wrapped responses. The follow-up design question is naming and semantics: this adapter-levelselectis a row projection for DB materialization, not exactly TanStack Query'sselect.A future additive API could make this more explicit, for example:
The optional reverse projection matters for direct writes and Query cache patching. Without a lawful reverse projection and subset-membership information, invalidating affected queries is safer than fabricating a wrapped cache response.
This PR should be additive and should not remove the existing
selectoption.PR 5: runtime binding / request-local QueryClient scope
Related issue: #436
A collection definition should be separable from runtime state.
QueryClientis runtime state and is often request-scoped on the server.A future additive direction could look like:
This would avoid process-global QueryClient assumptions and support TanStack Start loaders, SSR isolation, tests with multiple QueryClients, auth/account switching, and deterministic disposal.
PR 6: collection families and business scope
Related issue: #652
Separate two kinds of parameterization:
A collection-family API could make business scope first-class while keeping query-driven subset state explicit. This avoids forcing users to encode unrelated concepts into one anonymous
metabag or into ad hoc closures.PR 7: internal query lease manager
Related issue: #350, lifecycle portion
The adapter currently has to coordinate observers, unsubscribers, query-to-row ownership, row-to-query ownership, reference counts, retained query state, and retention timers. Those responsibilities would be easier to reason about if centered around a query/subset lease object.
A lease model should distinguish three lifetimes:
It should also make cancellation and late network results generation-safe, so a stale result cannot mutate rows after cancellation, disposal, or recreation.
Non-goals
selectbehavior without migration.metabehavior without migration.Questions for maintainers