Skip to content

feat: Implement BaseDataService#8039

Merged
FrederikBolding merged 59 commits intomainfrom
fb/data-service-base
Mar 24, 2026
Merged

feat: Implement BaseDataService#8039
FrederikBolding merged 59 commits intomainfrom
fb/data-service-base

Conversation

@FrederikBolding
Copy link
Member

@FrederikBolding FrederikBolding commented Feb 25, 2026

Explanation

This PR implements BaseDataService and a function to wrap QueryClient to proxy requests accordingly.

The BaseDataService, similarly to the BaseController provides the framework for building a service that can be registered and accessed via the messenger system, but also provides guarantees about per-request deduping, retries, caching, invalidation, state-while-revalidate etc via @tanstack/query-core.

The BaseDataService provides two utilities for this: fetchQuery and fetchInfiniteQuery, which is similar but one is separated for special pagination behaviour. Each service has its own cache for the APIs that it exposes that must also be synchronized with the UI processes. To facilitate this synchronization, the BaseDataService also automatically provides a cacheUpdate event.

The overall goal of the PR is to provide a base layer that can keep as much compatibility as possible with native TanStack Query while also simultaneously allowing us to have one source of truth per data service.

The synchronization is achieved via a special QueryClient created by createUIQueryClient, which wraps functionality such as cache invalidation, provides the default proxied fetch behaviour and subscribes to cache updates from data services that it is observing (e.g. has active queries for).

References

https://consensyssoftware.atlassian.net/browse/WPC-445

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Introduces new shared data-service/query infrastructure (caching, retries/circuit breaking, invalidation, cross-process cache hydration) that can affect data freshness and event synchronization if misused, though changes are isolated to new packages with test coverage.

Overview
Adds an initial implementation of @metamask/base-data-service, introducing BaseDataService built on @tanstack/query-core with fetchQuery/fetchInfiniteQuery, service-policy-wrapped execution (retries/circuit breaking), a registered :invalidateQueries action, and automatic :cacheUpdated/granular :cacheUpdated:${hash} events with dehydrated cache state.

Adds @metamask/react-data-query utilities to consume these services from UI code: createUIQueryClient proxies TanStack queries through a messenger, subscribes/unsubscribes to per-query cache update events to hydrate/remove cached entries, and forwards invalidateQueries to the underlying service; also adds typed useQuery/useInfiniteQuery wrappers.

Updates package metadata/build references, adds dependencies (TanStack v4, controller-utils, messenger, nock, etc.), and adjusts Yarn constraints to allow the TanStack v4 range and React peer deps without requiring devDependency installs.

Written by Cursor Bugbot for commit 0ae98c7. This will update automatically on new commits. Configure here.

@socket-security
Copy link

socket-security bot commented Feb 25, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​tanstack/​query-core@​4.43.0801007699100
Added@​tanstack/​react-query@​4.43.010010089100100

View full report

Base automatically changed from fb/init-base-data-service to main February 25, 2026 15:39
@FrederikBolding FrederikBolding force-pushed the fb/data-service-base branch 3 times, most recently from 1b629b1 to c19b3d7 Compare February 26, 2026 18:36
@FrederikBolding
Copy link
Member Author

@metamaskbot publish-previews

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-ee1fa5d01",
  "@metamask-previews/accounts-controller": "36.0.1-preview-ee1fa5d01",
  "@metamask-previews/address-book-controller": "7.0.1-preview-ee1fa5d01",
  "@metamask-previews/ai-controllers": "0.1.0-preview-ee1fa5d01",
  "@metamask-previews/analytics-controller": "1.0.0-preview-ee1fa5d01",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-ee1fa5d01",
  "@metamask-previews/announcement-controller": "8.0.0-preview-ee1fa5d01",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-ee1fa5d01",
  "@metamask-previews/approval-controller": "8.0.0-preview-ee1fa5d01",
  "@metamask-previews/assets-controller": "2.1.0-preview-ee1fa5d01",
  "@metamask-previews/assets-controllers": "100.0.3-preview-ee1fa5d01",
  "@metamask-previews/base-controller": "9.0.0-preview-ee1fa5d01",
  "@metamask-previews/base-data-service": "0.0.0-preview-ee1fa5d01",
  "@metamask-previews/bridge-controller": "67.3.0-preview-ee1fa5d01",
  "@metamask-previews/bridge-status-controller": "67.0.1-preview-ee1fa5d01",
  "@metamask-previews/build-utils": "3.0.4-preview-ee1fa5d01",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-ee1fa5d01",
  "@metamask-previews/claims-controller": "0.4.2-preview-ee1fa5d01",
  "@metamask-previews/client-controller": "1.0.0-preview-ee1fa5d01",
  "@metamask-previews/compliance-controller": "1.0.1-preview-ee1fa5d01",
  "@metamask-previews/composable-controller": "12.0.0-preview-ee1fa5d01",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-ee1fa5d01",
  "@metamask-previews/controller-utils": "11.19.0-preview-ee1fa5d01",
  "@metamask-previews/core-backend": "6.0.0-preview-ee1fa5d01",
  "@metamask-previews/delegation-controller": "2.0.1-preview-ee1fa5d01",
  "@metamask-previews/earn-controller": "11.1.1-preview-ee1fa5d01",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-ee1fa5d01",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-ee1fa5d01",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-ee1fa5d01",
  "@metamask-previews/ens-controller": "19.0.3-preview-ee1fa5d01",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-ee1fa5d01",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-ee1fa5d01",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-ee1fa5d01",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-ee1fa5d01",
  "@metamask-previews/foundryup": "1.0.1-preview-ee1fa5d01",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-ee1fa5d01",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-ee1fa5d01",
  "@metamask-previews/json-rpc-engine": "10.2.2-preview-ee1fa5d01",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-ee1fa5d01",
  "@metamask-previews/keyring-controller": "25.1.0-preview-ee1fa5d01",
  "@metamask-previews/logging-controller": "7.0.1-preview-ee1fa5d01",
  "@metamask-previews/message-manager": "14.1.0-preview-ee1fa5d01",
  "@metamask-previews/messenger": "0.3.0-preview-ee1fa5d01",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-ee1fa5d01",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-ee1fa5d01",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-ee1fa5d01",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-ee1fa5d01",
  "@metamask-previews/name-controller": "9.0.0-preview-ee1fa5d01",
  "@metamask-previews/network-controller": "30.0.0-preview-ee1fa5d01",
  "@metamask-previews/network-enablement-controller": "4.1.2-preview-ee1fa5d01",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-ee1fa5d01",
  "@metamask-previews/permission-controller": "12.2.0-preview-ee1fa5d01",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-ee1fa5d01",
  "@metamask-previews/perps-controller": "0.0.0-preview-ee1fa5d01",
  "@metamask-previews/phishing-controller": "16.3.0-preview-ee1fa5d01",
  "@metamask-previews/polling-controller": "16.0.3-preview-ee1fa5d01",
  "@metamask-previews/preferences-controller": "22.1.0-preview-ee1fa5d01",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-ee1fa5d01",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-ee1fa5d01",
  "@metamask-previews/ramps-controller": "10.0.0-preview-ee1fa5d01",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-ee1fa5d01",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-ee1fa5d01",
  "@metamask-previews/sample-controllers": "4.0.3-preview-ee1fa5d01",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-ee1fa5d01",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-ee1fa5d01",
  "@metamask-previews/shield-controller": "5.0.1-preview-ee1fa5d01",
  "@metamask-previews/signature-controller": "39.0.4-preview-ee1fa5d01",
  "@metamask-previews/storage-service": "1.0.0-preview-ee1fa5d01",
  "@metamask-previews/subscription-controller": "6.0.0-preview-ee1fa5d01",
  "@metamask-previews/transaction-controller": "62.19.0-preview-ee1fa5d01",
  "@metamask-previews/transaction-pay-controller": "16.1.0-preview-ee1fa5d01",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-ee1fa5d01"
}

@FrederikBolding
Copy link
Member Author

@metamaskbot publish-previews

@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-5111712",
  "@metamask-previews/accounts-controller": "36.0.1-preview-5111712",
  "@metamask-previews/address-book-controller": "7.0.1-preview-5111712",
  "@metamask-previews/ai-controllers": "0.1.0-preview-5111712",
  "@metamask-previews/analytics-controller": "1.0.0-preview-5111712",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-5111712",
  "@metamask-previews/announcement-controller": "8.0.0-preview-5111712",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-5111712",
  "@metamask-previews/approval-controller": "8.0.0-preview-5111712",
  "@metamask-previews/assets-controller": "2.2.0-preview-5111712",
  "@metamask-previews/assets-controllers": "100.0.3-preview-5111712",
  "@metamask-previews/base-controller": "9.0.0-preview-5111712",
  "@metamask-previews/base-data-service": "0.0.0-preview-5111712",
  "@metamask-previews/bridge-controller": "67.4.0-preview-5111712",
  "@metamask-previews/bridge-status-controller": "67.0.1-preview-5111712",
  "@metamask-previews/build-utils": "3.0.4-preview-5111712",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-5111712",
  "@metamask-previews/claims-controller": "0.4.2-preview-5111712",
  "@metamask-previews/client-controller": "1.0.0-preview-5111712",
  "@metamask-previews/compliance-controller": "1.0.1-preview-5111712",
  "@metamask-previews/composable-controller": "12.0.0-preview-5111712",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-5111712",
  "@metamask-previews/controller-utils": "11.19.0-preview-5111712",
  "@metamask-previews/core-backend": "6.0.0-preview-5111712",
  "@metamask-previews/delegation-controller": "2.0.1-preview-5111712",
  "@metamask-previews/earn-controller": "11.1.1-preview-5111712",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-5111712",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-5111712",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-5111712",
  "@metamask-previews/ens-controller": "19.0.3-preview-5111712",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-5111712",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-5111712",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-5111712",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-5111712",
  "@metamask-previews/foundryup": "1.0.1-preview-5111712",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-5111712",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-5111712",
  "@metamask-previews/geolocation-controller": "0.0.0-preview-5111712",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-5111712",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-5111712",
  "@metamask-previews/keyring-controller": "25.1.0-preview-5111712",
  "@metamask-previews/logging-controller": "7.0.1-preview-5111712",
  "@metamask-previews/message-manager": "14.1.0-preview-5111712",
  "@metamask-previews/messenger": "0.3.0-preview-5111712",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-5111712",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-5111712",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-5111712",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-5111712",
  "@metamask-previews/name-controller": "9.0.0-preview-5111712",
  "@metamask-previews/network-controller": "30.0.0-preview-5111712",
  "@metamask-previews/network-enablement-controller": "4.1.2-preview-5111712",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-5111712",
  "@metamask-previews/permission-controller": "12.2.0-preview-5111712",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-5111712",
  "@metamask-previews/perps-controller": "0.0.0-preview-5111712",
  "@metamask-previews/phishing-controller": "16.3.0-preview-5111712",
  "@metamask-previews/polling-controller": "16.0.3-preview-5111712",
  "@metamask-previews/preferences-controller": "22.1.0-preview-5111712",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-5111712",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-5111712",
  "@metamask-previews/ramps-controller": "10.0.0-preview-5111712",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-5111712",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-5111712",
  "@metamask-previews/sample-controllers": "4.0.3-preview-5111712",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-5111712",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-5111712",
  "@metamask-previews/shield-controller": "5.0.1-preview-5111712",
  "@metamask-previews/signature-controller": "39.0.4-preview-5111712",
  "@metamask-previews/storage-service": "1.0.0-preview-5111712",
  "@metamask-previews/subscription-controller": "6.0.0-preview-5111712",
  "@metamask-previews/transaction-controller": "62.19.0-preview-5111712",
  "@metamask-previews/transaction-pay-controller": "16.1.2-preview-5111712",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-5111712"
}

@FrederikBolding
Copy link
Member Author

@metamaskbot publish-previews

@FrederikBolding
Copy link
Member Author

@metamaskbot publish-previews

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "5.0.0-preview-00245ea",
  "@metamask-previews/accounts-controller": "37.0.0-preview-00245ea",
  "@metamask-previews/address-book-controller": "7.0.1-preview-00245ea",
  "@metamask-previews/ai-controllers": "0.2.0-preview-00245ea",
  "@metamask-previews/analytics-controller": "1.0.0-preview-00245ea",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-00245ea",
  "@metamask-previews/announcement-controller": "8.0.0-preview-00245ea",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-00245ea",
  "@metamask-previews/approval-controller": "8.0.0-preview-00245ea",
  "@metamask-previews/assets-controller": "2.3.0-preview-00245ea",
  "@metamask-previews/assets-controllers": "100.2.0-preview-00245ea",
  "@metamask-previews/base-controller": "9.0.0-preview-00245ea",
  "@metamask-previews/base-data-service": "0.0.0-preview-00245ea",
  "@metamask-previews/bridge-controller": "69.0.0-preview-00245ea",
  "@metamask-previews/bridge-status-controller": "68.0.1-preview-00245ea",
  "@metamask-previews/build-utils": "3.0.4-preview-00245ea",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-00245ea",
  "@metamask-previews/claims-controller": "0.4.2-preview-00245ea",
  "@metamask-previews/client-controller": "1.0.0-preview-00245ea",
  "@metamask-previews/compliance-controller": "1.0.1-preview-00245ea",
  "@metamask-previews/composable-controller": "12.0.0-preview-00245ea",
  "@metamask-previews/config-registry-controller": "0.1.0-preview-00245ea",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-00245ea",
  "@metamask-previews/controller-utils": "11.19.0-preview-00245ea",
  "@metamask-previews/core-backend": "6.1.0-preview-00245ea",
  "@metamask-previews/delegation-controller": "2.0.2-preview-00245ea",
  "@metamask-previews/earn-controller": "11.1.2-preview-00245ea",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-00245ea",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-00245ea",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-00245ea",
  "@metamask-previews/ens-controller": "19.0.3-preview-00245ea",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-00245ea",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-00245ea",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-00245ea",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-00245ea",
  "@metamask-previews/foundryup": "1.0.1-preview-00245ea",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-00245ea",
  "@metamask-previews/gator-permissions-controller": "2.1.0-preview-00245ea",
  "@metamask-previews/geolocation-controller": "0.1.1-preview-00245ea",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-00245ea",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-00245ea",
  "@metamask-previews/keyring-controller": "25.1.0-preview-00245ea",
  "@metamask-previews/logging-controller": "7.0.1-preview-00245ea",
  "@metamask-previews/message-manager": "14.1.0-preview-00245ea",
  "@metamask-previews/messenger": "0.3.0-preview-00245ea",
  "@metamask-previews/multichain-account-service": "7.1.0-preview-00245ea",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-00245ea",
  "@metamask-previews/multichain-network-controller": "3.0.5-preview-00245ea",
  "@metamask-previews/multichain-transactions-controller": "7.0.2-preview-00245ea",
  "@metamask-previews/name-controller": "9.0.0-preview-00245ea",
  "@metamask-previews/network-controller": "30.0.0-preview-00245ea",
  "@metamask-previews/network-enablement-controller": "4.2.0-preview-00245ea",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-00245ea",
  "@metamask-previews/permission-controller": "12.2.0-preview-00245ea",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-00245ea",
  "@metamask-previews/perps-controller": "1.0.0-preview-00245ea",
  "@metamask-previews/phishing-controller": "16.3.0-preview-00245ea",
  "@metamask-previews/polling-controller": "16.0.3-preview-00245ea",
  "@metamask-previews/preferences-controller": "23.0.0-preview-00245ea",
  "@metamask-previews/profile-metrics-controller": "3.0.2-preview-00245ea",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-00245ea",
  "@metamask-previews/ramps-controller": "10.2.0-preview-00245ea",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-00245ea",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-00245ea",
  "@metamask-previews/sample-controllers": "4.0.3-preview-00245ea",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-00245ea",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-00245ea",
  "@metamask-previews/shield-controller": "5.0.1-preview-00245ea",
  "@metamask-previews/signature-controller": "39.0.5-preview-00245ea",
  "@metamask-previews/storage-service": "1.0.0-preview-00245ea",
  "@metamask-previews/subscription-controller": "6.0.0-preview-00245ea",
  "@metamask-previews/transaction-controller": "62.21.0-preview-00245ea",
  "@metamask-previews/transaction-pay-controller": "16.4.1-preview-00245ea",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-00245ea"
}

@FrederikBolding FrederikBolding marked this pull request as ready for review March 10, 2026 15:30
@FrederikBolding FrederikBolding requested a review from a team as a code owner March 10, 2026 15:30
*/
const ALLOWED_INCONSISTENT_DEPENDENCIES = {
// '@metamask/json-rpc-engine': ['^9.0.3'],
'@tanstack/query-core': ['^4.43.0'],
Copy link
Member Author

Choose a reason for hiding this comment

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

core-backend will need to downgrade to use BaseDataService or wait until we are able to bump everywhere (blocked by extension not supporting React 18).

@mcmire
Copy link
Contributor

mcmire commented Mar 10, 2026

I know that TanStack Query is the motivation for this PR, but before we merge this I want to make sure that we've (at least briefly) considered how this new package would overlap with the other "data service layer" features which are already implemented and which we've discussed adding in the future. Namely:

  • Do we still want to encourage teams to use a Cockatiel policy when making HTTP requests? If so, would it be within the domain of this new package to make it easier for teams to do this? (For instance, perhaps the constructor could use createServicePolicy to initialize a policy which could be used when making HTTP requests inside of queryFn's, and perhaps the BaseDataService class could include callbacks for onBreak, onDegraded, etc. Also it might be worth moving createServicePolicy out of controller-utils into this package at some point.) Or, are we essentially deprecating Cockatiel policies in favor of whatever TanStack Query provides?
  • Where do existing patterns around fetching data — polling and websockets — come into play? If we were to provide official solutions for these patterns would they also eventually go into this package or somewhere?

I guess the theme of these questions is that I want to understand what the intended domain of this package is. Should it be only restricted to TanStack Query integration in the future — meaning that we may create other packages to solve other problems later — or should it be designed to encompass other things that are data-service-related in the future?

@Gudahtt
Copy link
Member

Gudahtt commented Mar 10, 2026

I was expecting that we'd continue to use Cockatiel. TanStack does have basic built-in retry functionality, but nothing remotely similar to what our current retry/circuit break policies do.

@Gudahtt
Copy link
Member

Gudahtt commented Mar 10, 2026

Related question: Are there any data services where this query-related functionality would not be useful? i.e. where extending this base class would be unwanted.

If so, we could rename this to BaseQueryService or something.

I think the answer here is "no" though. When wouldn't we want request deduplication, and easier-to-use caching options?

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

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

Still working through this PR, but made some more suggestions.

Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

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

Getting closer on this. I admit I'm not following the code in createUIQueryClient. The tests do help in demonstrating the behavior, but I feel like I need to read about observers to fully grasp it. That said, overall, I don't have any major concerns, just minor things. I'll try to do a final pass next week.

@FrederikBolding FrederikBolding requested a review from mcmire March 23, 2026 12:00
if (
!hasSubscription &&
event.type === 'observerAdded' &&
observerCount === 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this similar-ish to what we are doing in MessengerSubscriptions in the extension?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. Whenever the first consumer of a query is added, we add our subscription, when no consumers are left we can unsubscribe again.

) => void;
type JsonSubscriptionCallback = (data: Json) => void;

// TODO: Figure out if we can replace with a better Messenger type
Copy link
Contributor

@mcmire mcmire Mar 23, 2026

Choose a reason for hiding this comment

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

It seems like after the UI messenger integration is introduced, this could either be the UI messenger or a route messenger. Would that make sense or would we still need an abstract type here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Potentially. We would need the type to be generic enough that it can support any data service.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

@FrederikBolding FrederikBolding requested a review from mcmire March 24, 2026 09:43
Copy link
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

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

I'll still have to do more research on TanStack Query works but I'm caught up enough to have a rough understanding. I like how simple this is. LGTM.

@FrederikBolding FrederikBolding added this pull request to the merge queue Mar 24, 2026
Merged via the queue into main with commit dd11598 Mar 24, 2026
326 checks passed
@FrederikBolding FrederikBolding deleted the fb/data-service-base branch March 24, 2026 15:47
micaelae pushed a commit that referenced this pull request Mar 24, 2026
## Explanation

This PR implements `BaseDataService` and a function to wrap
`QueryClient` to proxy requests accordingly.

The `BaseDataService`, similarly to the `BaseController` provides the
framework for building a service that can be registered and accessed via
the messenger system, but also provides guarantees about per-request
deduping, retries, caching, invalidation, state-while-revalidate etc via
`@tanstack/query-core`.

The `BaseDataService` provides two utilities for this: `fetchQuery` and
`fetchInfiniteQuery`, which is similar but one is separated for special
pagination behaviour. Each service has its own cache for the APIs that
it exposes that must also be synchronized with the UI processes. To
facilitate this synchronization, the `BaseDataService` also
automatically provides a `cacheUpdate` event.

The overall goal of the PR is to provide a base layer that can keep as
much compatibility as possible with native TanStack Query while also
simultaneously allowing us to have one source of truth per data service.

The synchronization is achieved via a special `QueryClient` created by
`createUIQueryClient`, which wraps functionality such as cache
invalidation, provides the default proxied fetch behaviour and
subscribes to cache updates from data services that it is observing
(e.g. has active queries for).

## References

https://consensyssoftware.atlassian.net/browse/WPC-445

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [ ] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Introduces new shared data-service/query infrastructure (caching,
retries/circuit breaking, invalidation, cross-process cache hydration)
that can affect data freshness and event synchronization if misused,
though changes are isolated to new packages with test coverage.
> 
> **Overview**
> Adds an initial implementation of `@metamask/base-data-service`,
introducing `BaseDataService` built on `@tanstack/query-core` with
`fetchQuery`/`fetchInfiniteQuery`, service-policy-wrapped execution
(retries/circuit breaking), a registered `:invalidateQueries` action,
and automatic `:cacheUpdated`/granular `:cacheUpdated:${hash}` events
with dehydrated cache state.
> 
> Adds `@metamask/react-data-query` utilities to consume these services
from UI code: `createUIQueryClient` proxies TanStack queries through a
messenger, subscribes/unsubscribes to per-query cache update events to
hydrate/remove cached entries, and forwards `invalidateQueries` to the
underlying service; also adds typed `useQuery`/`useInfiniteQuery`
wrappers.
> 
> Updates package metadata/build references, adds dependencies (TanStack
v4, controller-utils, messenger, nock, etc.), and adjusts Yarn
constraints to allow the TanStack v4 range and React peer deps without
requiring devDependency installs.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0ae98c7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants