T
TanStack2y ago
firm-tan

Dynamically prefix all queryKeys

We have a setup whereby our APIs return different data, based on which account_id the JWT is issued for. E.g. posts.list returns posts only for the authorised account_id without having to specify any filters, etc. Is there a good way to dynamically transform all query keys and prefix them with the account_id, so that we wouldn't have to manually update all queries to do so, and avoid us missing to add it, in which case we can accidentally display stale data from a previously loaded account. I know we could flush the cache every time the account_id changes, but would much prefer to keep the caching in place.
10 Replies
jolly-crimson
jolly-crimson2y ago
I‘d be concerned if the cache persists between user logout / login
firm-tan
firm-tanOP2y ago
it doesn't, we purge the cache on logout
jolly-crimson
jolly-crimson2y ago
But nonetheless: this is a pattern that could help you: https://tkdodo.eu/blog/the-query-options-api#query-factories
The Query Options API
v5 brought a new, powerful API, especially if you're using React Query with TypeScript...
firm-tan
firm-tanOP2y ago
Found a related question on github: https://github.com/TanStack/query/discussions/3743 We want to avoid just that – having to pass around accountIds, and manually prefixing them to frontend/backend functionality. By limiting the surface area of account_id's, we can have a much more easily testable and secure system. Manual prefixing is much more prone to errors and demands for individual tests on a much wider surface area.
GitHub
Global scope for react query keys? · TanStack query · Discussion #3...
In most apps there is the need to scope cached queries by default. Let's say you allow users to use multiple accounts. You'd probably want to scope each and every query to the account the u...
firm-tan
firm-tanOP2y ago
I think both of these options are too error-prone. Easy to forget passing in accountId, and difficult to globally type queryKeys to start with an accountId.
jolly-crimson
jolly-crimson2y ago
Did you read the link I posted?
firm-tan
firm-tanOP2y ago
I did, and I like the pattern. But I'm not sure it addresses my original question about scoping. I guess I could make a function that outputs a factory, but then again we also use e.g. trpc alongside other queries, which makes things a bit more complicated.
jolly-crimson
jolly-crimson2y ago
Just put the accountId in the first all queryKey?! I don’t know about trpc though
firm-tan
firm-tanOP2y ago
It's an option, but far from a global guarantee that all implementations are scoped to the current account. Would rather flush the cache on an account change vs. this, still. If I think about e.g. server-side caching with Redis or other means, I would always namespace on the cache client level, rather than individual implementations. The surface area for testing for account isolation would be 1 instead of n of keys. Found a potential solution – defining a custom queryKeyHashFn. Alternatively, initiating a new instance of queryClient per account, and persisting them under different keys. Works perfectly well. Persisting, and everything. TRPC + regular useQuery calls both with one go.
jolly-crimson
jolly-crimson2y ago
Nice solution!!

Did you find this page helpful?