Operating System
MacOS Sequia 15.7.3
Environment (if applicable)
React Native (Expo) , Firebase Data Connect, Tanstack Query v5
Firebase SDK Version
12.10.0
Firebase SDK Product(s)
Database, DataConnect
Project Tooling
React Native app using Expo
Detailed Problem Description
Detailed Problem Description
When using Firebase Data Connect with the React TanStack Query wrapper, queries that use the generated React Hooks always default to the internal Data Connect cache because the wrapper does not forward fetchPolicy to the underlying executeQuery call.
This results in stale results being returned even when TanStack Query invalidates or refetches the query.
What we were trying to achieve
Use Firebase Data Connect together with TanStack Query and perform:
- optimistic updates
- query invalidation
- refetching from the server
These are standard patterns supported by TanStack Query.
What actually happens
Even when React Query refetches the query, the Firebase SDK returns cached results instead of hitting the server.
This occurs because executeQuery defaults to: fetchPolicy = PREFER_CACHE
and the React wrapper never forwards a fetchPolicy option.
Because of this, the Data Connect QueryManager cache overrides React Query's cache lifecycle.
This results in behavior like:
- Perform optimistic update
- Invalidate query
- React Query refetches
- Data Connect returns cached result instead of server result
- UI reverts to stale state
Root Cause
The React wrapper (useDataConnectQuery) calls executeQuery without forwarding options:
However the SDK implementation expects:
executeQuery(queryRef, options)
And default to: fetchPolicy = PREFER_CACHE (when options are not provided)
Because the wrapper does not expose or forward fetchPolicy, developers cannot control the caching behavior.
Relevant Code Locations
Generated React hook
packages/mobile-app/src/dataconnect-generated/react/index.cjs.js
Example generated hook:
exports.useGetThings = function useGetThings(dcOrVars, varsOrOptions, options) {
const { dc: dcInstance, vars: inputVars, options: inputOpts } =
validateReactArgs(connectorConfig, dcOrVars, varsOrOptions, options, true, true);
const ref = getThingsRef(dcInstance, inputVars);
return useDataConnectQuery(ref, inputOpts, CallerSdkTypeEnum.GeneratedReact);
}
This passes options into useDataConnectQuery.
useQueryResult from Tanstack data-connect
node_modules/@tanstack-query-firebase/react/dist/data-connect/index.js
Relevant Section
const useQueryResult = useQuery({
...options,
initialData,
queryKey: options?.queryKey ?? [ref.name, ref.variables || null],
queryFn: async () => {
const response = await executeQuery2(ref);
setDataConnectResult(response);
return {
...response.data
};
}
});
The wrapper calls: executeQuery2(ref) but does not pass any options.
This prevents the user from setting: fetchPolicy
Data Connect SDK implementation
node_modules/@firebase/data-connect/dist/index.cjs.js
function executeQuery(queryRef, options) {
if (queryRef.refType !== QUERY_STR) {
return Promise.reject(new DataConnectError(Code.INVALID_ARGUMENT, `ExecuteQuery can only execute query operations`));
}
const queryManager = queryRef.dataConnect._queryManager;
const fetchPolicy = options?.fetchPolicy ?? QueryFetchPolicy.PREFER_CACHE;
switch (fetchPolicy) {
case QueryFetchPolicy.SERVER_ONLY:
return queryManager.fetchServerResults(queryRef);
case QueryFetchPolicy.CACHE_ONLY:
return queryManager.fetchCacheResults(queryRef, true);
case QueryFetchPolicy.PREFER_CACHE:
return queryManager.preferCacheResults(queryRef, false);
default:
throw new DataConnectError(Code.INVALID_ARGUMENT, `Invalid fetch policy: ${fetchPolicy}`);
}
}
Because executeQuery is called without options, the default policy is always: PREFER_CACHE
Behavior
- Fetch query using generated hook
- Perform optimistic update via React Query
- Invalidate query
- Query refetches
- Data Connect returns cached result instead of server result
Temporary Workaround
Manually patching the wrapper to pass a fetch policy resolves the issue.
Example change:
const response = await executeQuery2(ref, {
fetchPolicy: QueryFetchPolicy.SERVER_ONLY
});
After this change, queries correctly hit the server and React Query behaves as expected.
Expected Behavior
The React wrapper should allow users to control fetchPolicy, either by:
forwarding fetchPolicy from hook options
exposing a Data Connect options object
defaulting to SERVER_ONLY when used with TanStack Query
Actual Behavior
useDataConnectQuery prevents any control of fetchPolicy, forcing: PREFER_CACHE
which conflicts with TanStack Query's caching model.
Summary
Current architecture results in two competing cache layers:
React Query cache
+
Firebase Data Connect QueryManager cache
Because fetchPolicy cannot be configured, developers cannot disable the Data Connect cache when using TanStack Query.
Forwarding fetchPolicy from useDataConnectQuery to executeQuery would resolve this issue.
Steps and code to reproduce issue
Steps and Code to Reproduce Issue
1. Schema
type Thing @table(key: ["id"]) {
id: UUID! @default(expr: "uuidV4()")
text: String!
}
2. Query
query GetThings {
things {
id
text
}
}
3. Mutation
mutation UpdateThing($id: UUID!, $text: String!) {
thing_update(
key: { id: $id }
data: { text: $text }
)
}
4. Fetch Data Using Generated Hook
const { data } = useGetThings();
5. Perform Optimistic Update
const queryClient = useQueryClient();
const mutation = useUpdateThing({
onMutate: async (variables) => {
await queryClient.cancelQueries(["GetThings"]);
const previous = queryClient.getQueryData(["GetThings"]);
queryClient.setQueryData(["GetThings"], (old) => ({
things: old.things.map((t) =>
t.id === variables.id ? { ...t, text: variables.text } : t
)
}));
return { previous };
},
onError: (_err, _vars, context) => {
queryClient.setQueryData(["GetThings"], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries(["GetThings"]);
}
});
6. Result
GetThings is fetched using the generated Data Connect hook.
- A mutation performs an optimistic update.
invalidateQueries(["GetThings"]) triggers a refetch.
- The query function executes again.
- Firebase Data Connect returns cached results instead of server results.
Console output shows:
The UI reverts to stale data even though React Query refetched the query.
7. Workaround
If the wrapper is patched to call:
executeQuery(ref, { fetchPolicy: QueryFetchPolicy.SERVER_ONLY })
the query correctly fetches fresh server data and the issue disappears.
Operating System
MacOS Sequia 15.7.3
Environment (if applicable)
React Native (Expo) , Firebase Data Connect, Tanstack Query v5
Firebase SDK Version
12.10.0
Firebase SDK Product(s)
Database, DataConnect
Project Tooling
React Native app using Expo
Detailed Problem Description
Detailed Problem Description
When using Firebase Data Connect with the React TanStack Query wrapper, queries that use the generated React Hooks always default to the internal Data Connect cache because the wrapper does not forward
fetchPolicyto the underlyingexecuteQuerycall.This results in stale results being returned even when TanStack Query invalidates or refetches the query.
What we were trying to achieve
Use Firebase Data Connect together with TanStack Query and perform:
These are standard patterns supported by TanStack Query.
What actually happens
Even when React Query refetches the query, the Firebase SDK returns cached results instead of hitting the server.
This occurs because
executeQuerydefaults to: fetchPolicy =PREFER_CACHEand the React wrapper never forwards a
fetchPolicyoption.Because of this, the Data Connect QueryManager cache overrides React Query's cache lifecycle.
This results in behavior like:
Root Cause
The React wrapper (
useDataConnectQuery) callsexecuteQuerywithout forwarding options:However the SDK implementation expects:
And default to: fetchPolicy =
PREFER_CACHE(when options are not provided)Because the wrapper does not expose or forward
fetchPolicy, developers cannot control the caching behavior.Relevant Code Locations
Generated React hook
packages/mobile-app/src/dataconnect-generated/react/index.cjs.js
Example generated hook:
This passes options into useDataConnectQuery.
useQueryResult from Tanstack data-connect
node_modules/@tanstack-query-firebase/react/dist/data-connect/index.js
Relevant Section
The wrapper calls: executeQuery2(ref) but does not pass any options.
This prevents the user from setting: fetchPolicy
Data Connect SDK implementation
node_modules/@firebase/data-connect/dist/index.cjs.js
Because executeQuery is called without options, the default policy is always: PREFER_CACHE
Behavior
Temporary Workaround
Manually patching the wrapper to pass a fetch policy resolves the issue.
Example change:
After this change, queries correctly hit the server and React Query behaves as expected.
Expected Behavior
The React wrapper should allow users to control fetchPolicy, either by:
forwarding fetchPolicy from hook options
exposing a Data Connect options object
defaulting to SERVER_ONLY when used with TanStack Query
Actual Behavior
useDataConnectQuery prevents any control of fetchPolicy, forcing:
PREFER_CACHEwhich conflicts with TanStack Query's caching model.
Summary
Current architecture results in two competing cache layers:
React Query cache
+
Firebase Data Connect QueryManager cache
Because fetchPolicy cannot be configured, developers cannot disable the Data Connect cache when using TanStack Query.
Forwarding fetchPolicy from useDataConnectQuery to executeQuery would resolve this issue.
Steps and code to reproduce issue
Steps and Code to Reproduce Issue
1. Schema
2. Query
3. Mutation
4. Fetch Data Using Generated Hook
5. Perform Optimistic Update
6. Result
GetThingsis fetched using the generated Data Connect hook.invalidateQueries(["GetThings"])triggers a refetch.Console output shows:
The UI reverts to stale data even though React Query refetched the query.
7. Workaround
If the wrapper is patched to call:
the query correctly fetches fresh server data and the issue disappears.