feat(core-backend): expose query options#7928
Merged
Conversation
84c13aa to
b1e1749
Compare
b1e1749 to
1a23080
Compare
Prithpal-Sooriya
previously approved these changes
Feb 13, 2026
1a23080 to
00d3be7
Compare
2cff407 to
cd9ed57
Compare
cd9ed57 to
45a8b60
Compare
Prithpal-Sooriya
approved these changes
Feb 13, 2026
khanti42
pushed a commit
that referenced
this pull request
Feb 20, 2026
# Improve @metamask/core-backend: ApiPlatformClientService + Query
Options
## Explanation
### What is the current state of things and why does it need to change?
- **ApiPlatformClient** is today passed by reference (e.g. into
`AssetsController` and its data sources). Consumers that want to use the
API client must receive it via constructor or similar injection. There
was no way to obtain the client through the messenger only, which is
useful when wiring services in a controller-based app without
introducing a dedicated controller for the client.
- **TanStack Query options** used internally by the API clients (e.g.
for `fetchV5MultiAccountBalances`, `fetchV3SpotPrices`) were not
exposed. Consumers that want to use `useQuery`, `useInfiniteQuery`, or
`useSuspenseQuery` with the same query keys and options had to duplicate
or reverse-engineer them.
### What is the solution your changes offer and how does it work?
1. **ApiPlatformClientService**
A new **service** (not a controller) in `@metamask/core-backend` that
holds an `ApiPlatformClient` and exposes it via the messenger, following
the same pattern as `SampleGasPricesService`. The service is constructed
with `messenger` plus `ApiPlatformClientOptions` (e.g. `clientProduct`,
`getBearerToken`). It registers a single action,
`ApiPlatformClientService:getApiPlatformClient`, which returns the
shared client. Consumers can call
`messenger.call('ApiPlatformClientService:getApiPlatformClient')` to get
the client and then use `client.accounts`, `client.prices`,
`client.token`, and `client.tokens` as usual.
2. **Exported query options**
For every fetch method on the Accounts, Prices, Token, and Tokens API
clients, a corresponding **`get*QueryOptions`** helper is exported (e.g.
`getV5MultiAccountBalancesQueryOptions`, `getV3SpotPricesQueryOptions`).
Each returns the same TanStack Query options object (queryKey, queryFn,
staleTime, gcTime, etc.) that the client uses internally. The existing
fetch methods now call these helpers and pass the result to
`queryClient.fetchQuery`, so behavior is unchanged. Consumers can call
the same helpers and pass the result to `useQuery`, `useInfiniteQuery`,
`useSuspenseQuery`, or any API that accepts TanStack Query options.
### Are there any changes whose purpose might not be obvious to those
unfamiliar with the domain?
- **Why a service instead of a controller?**
The goal is to expose a single capability (obtaining the API client)
without persistent state or controller lifecycle. The [Core data-service
guidelines](https://github.com/MetaMask/core/blob/main/docs/controller-guidelines.md)
and the sample package use a **service** (plain class +
`registerMethodActionHandlers`) for this; the same pattern is used by
`BackendWebSocketService` and `AccountActivityService` in core-backend.
- **Why export query options instead of only fetch methods?**
UI that uses React Query (or similar) needs the **options object**
(queryKey, queryFn, staleTime, etc.) to plug into `useQuery` /
`useInfiniteQuery` / `useSuspenseQuery`. Exposing it keeps a single
source of truth for keys and behavior and avoids drift between “fetch”
and “hook” usage.
### If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
All changes are confined to `@metamask/core-backend`. No other packages
were modified.
### If you had to upgrade a dependency, why did you do so?
No dependency upgrades in this PR.
---
## References
- Fixes: (add issue number if applicable)
- Related: (e.g. consumer PRs that will use `ApiPlatformClientService`
or `get*QueryOptions`)
---
## Checklist
- [x] I've updated the test suite for new or updated code as appropriate
- Added `ApiPlatformClientService.test.ts`; fixed lint in that file.
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- JSDoc added for all `get*QueryOptions`; service and README already
document usage.
- [x] 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)
- `packages/core-backend/CHANGELOG.md`: entries for
ApiPlatformClientService and for exported query options.
- [ ] 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
- N/A. No breaking changes.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Medium risk due to multiple public API surface changes (renamed
params, removed methods/options, and altered error semantics) that can
break downstream callers and caching keys if not updated consistently.
>
> **Overview**
> Introduces `ApiPlatformClientService`, a new messenger-exposed service
that provides a shared `ApiPlatformClient` instance via
`ApiPlatformClientService:getApiPlatformClient`.
>
> Across `accounts`, `prices`, `token`, and `tokens` clients, adds
`get*QueryOptions` helpers for each endpoint and extends `FetchOptions`
to accept TanStack Query options; `getQueryOptionsOverrides` is
added/exported to merge caller overrides while preserving client-owned
`queryKey`/`queryFn` and consistent `staleTime`/`gcTime` defaults.
>
> Makes **breaking** API adjustments to align with backend specs: merges
`fetchV2BalancesWithOptions` into `fetchV2Balances`, updates v4
multi-account transactions to use `accountAddresses` and expanded filter
params (removing `includeValueTransfers`), and changes some edge
behavior (short-circuit empty required inputs to return empty results;
relationship and `fetchV1TokenPrice` error paths now throw instead of
returning sentinel values).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
45a8b60. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Improve @metamask/core-backend: ApiPlatformClientService + Query Options
Explanation
What is the current state of things and why does it need to change?
AssetsControllerand its data sources). Consumers that want to use the API client must receive it via constructor or similar injection. There was no way to obtain the client through the messenger only, which is useful when wiring services in a controller-based app without introducing a dedicated controller for the client.fetchV5MultiAccountBalances,fetchV3SpotPrices) were not exposed. Consumers that want to useuseQuery,useInfiniteQuery, oruseSuspenseQuerywith the same query keys and options had to duplicate or reverse-engineer them.What is the solution your changes offer and how does it work?
ApiPlatformClientService
A new service (not a controller) in
@metamask/core-backendthat holds anApiPlatformClientand exposes it via the messenger, following the same pattern asSampleGasPricesService. The service is constructed withmessengerplusApiPlatformClientOptions(e.g.clientProduct,getBearerToken). It registers a single action,ApiPlatformClientService:getApiPlatformClient, which returns the shared client. Consumers can callmessenger.call('ApiPlatformClientService:getApiPlatformClient')to get the client and then useclient.accounts,client.prices,client.token, andclient.tokensas usual.Exported query options
For every fetch method on the Accounts, Prices, Token, and Tokens API clients, a corresponding
get*QueryOptionshelper is exported (e.g.getV5MultiAccountBalancesQueryOptions,getV3SpotPricesQueryOptions). Each returns the same TanStack Query options object (queryKey, queryFn, staleTime, gcTime, etc.) that the client uses internally. The existing fetch methods now call these helpers and pass the result toqueryClient.fetchQuery, so behavior is unchanged. Consumers can call the same helpers and pass the result touseQuery,useInfiniteQuery,useSuspenseQuery, or any API that accepts TanStack Query options.Are there any changes whose purpose might not be obvious to those unfamiliar with the domain?
Why a service instead of a controller?
The goal is to expose a single capability (obtaining the API client) without persistent state or controller lifecycle. The Core data-service guidelines and the sample package use a service (plain class +
registerMethodActionHandlers) for this; the same pattern is used byBackendWebSocketServiceandAccountActivityServicein core-backend.Why export query options instead of only fetch methods?
UI that uses React Query (or similar) needs the options object (queryKey, queryFn, staleTime, etc.) to plug into
useQuery/useInfiniteQuery/useSuspenseQuery. Exposing it keeps a single source of truth for keys and behavior and avoids drift between “fetch” and “hook” usage.If your primary goal was to update one package but you found you had to update another one along the way, why did you do so?
All changes are confined to
@metamask/core-backend. No other packages were modified.If you had to upgrade a dependency, why did you do so?
No dependency upgrades in this PR.
References
ApiPlatformClientServiceorget*QueryOptions)Checklist
ApiPlatformClientService.test.ts; fixed lint in that file.get*QueryOptions; service and README already document usage.packages/core-backend/CHANGELOG.md: entries for ApiPlatformClientService and for exported query options.Note
Medium Risk
Medium risk due to multiple public API surface changes (renamed params, removed methods/options, and altered error semantics) that can break downstream callers and caching keys if not updated consistently.
Overview
Introduces
ApiPlatformClientService, a new messenger-exposed service that provides a sharedApiPlatformClientinstance viaApiPlatformClientService:getApiPlatformClient.Across
accounts,prices,token, andtokensclients, addsget*QueryOptionshelpers for each endpoint and extendsFetchOptionsto accept TanStack Query options;getQueryOptionsOverridesis added/exported to merge caller overrides while preserving client-ownedqueryKey/queryFnand consistentstaleTime/gcTimedefaults.Makes breaking API adjustments to align with backend specs: merges
fetchV2BalancesWithOptionsintofetchV2Balances, updates v4 multi-account transactions to useaccountAddressesand expanded filter params (removingincludeValueTransfers), and changes some edge behavior (short-circuit empty required inputs to return empty results; relationship andfetchV1TokenPriceerror paths now throw instead of returning sentinel values).Written by Cursor Bugbot for commit 45a8b60. This will update automatically on new commits. Configure here.