docs/addons/shared-query-client-design.md
Each sandboxed addon owns one TanStack Query QueryClient. The client is reused
across that addon's route renders, then cleared when the sandbox stops. It is
not the main application's client and does not share the host's in-memory cache.
const addonQueryClient = ctx.api.query.getClient();
function AddonRoot() {
return (
<QueryClientProvider client={addonQueryClient}>
<AddonPage />
</QueryClientProvider>
);
}
The isolation is intentional: a query cannot leak data or observers between addons, and removing an addon also removes its cache.
The sandbox augments the addon's client so calls to invalidateQueries() and
refetchQueries() do two things:
ctx.api.query.The convenience methods below use the same bridge:
ctx.api.query.invalidateQueries(["accounts"]);
ctx.api.query.refetchQueries(["portfolio", "holdings"]);
This keeps mutations initiated by an addon coherent with host views without exposing the host client across the iframe boundary. The reverse direction is not automatic: a host cache invalidation does not directly mutate an addon's local cache.
Use the domain event APIs when addon data must react to work initiated elsewhere in Wealthfolio. Invalidate the addon's local query after receiving the event:
export async function enable(ctx: AddonContext) {
const queryClient = ctx.api.query.getClient() as QueryClient;
const unlisten = await ctx.api.events.portfolio.onUpdateComplete(() => {
void queryClient.invalidateQueries({ queryKey: ["portfolio"] });
});
ctx.onDisable(unlisten);
}
Do not rely on matching query keys alone to synchronize the two processes. Keys identify cache entries locally; events and the invalidation bridge coordinate changes across the sandbox boundary.
ctx.api.query.getClient() once and pass that client to
QueryClientProvider.QueryClient unless the addon deliberately needs a
separate cache.Addon mutation
↓
addon QueryClient invalidate/refetch
├─ updates addon-local observers
└─ RPC bridge → host QueryAPI → updates matching host queries
Host mutation
↓
host domain event → addon listener → addon-local invalidate/refetch