🧵 Composing RTK Query Endpoints for Richer Queries: A Proposal #4931
fernandocamargo
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi Redux Toolkit team (and maintainers and community as whole)! 👋
We’ve been experimenting with a pattern that composes RTK Query endpoints from one another, and it’s proven to be powerful, expressive, and DRY. Before we fully commit to this architecture, we’d love your thoughts on whether there are any hidden pitfalls.
🎯 The Goal
We want to build richer queries by composing simpler ones, without losing the benefits of RTK Query like caching, async state tracking, invalidation, and a central source of truth.
Let’s say we already have endpoints like:
We want to build:
…but instead of duplicating logic or re-fetching data from the backend, we compose the result from
getUsers
andgetDepartments
.✅ What This Looks Like
💡 Why We Like It
Here are some of the strongest advantages we’ve found.
1. No Duplication of Business Logic
2. Keeps RTK Query as the Source of Truth
All data fetching continues to go through RTK Query. The composed endpoint still benefits from:
keepUnusedDataFor
, etc.)msw
or similar)It avoids the temptation to do one-off manual compositions in
useEffect
or slices, which fragments the data layer.3. Reuses Cached Data
Composed endpoints call
.initiate()
on the base endpoints, meaning:4. Observability for Free
Because composed endpoints are still RTK Query endpoints, components using them get the full async state suite:
This gives excellent visibility into the request lifecycle — no need to invent custom hooks for polling, loading states, etc.
5. Simple Invalidations Cascade Naturally
You can tag your base endpoints and compose endpoints can still benefit:
So any mutation that invalidates
['Users']
also refreshesgetUsersWithDepartments
.6. Memory Efficiency via Shared References
One subtle but important benefit of this pattern is that composed endpoints reference the same data structures as their base endpoints, thanks to caching and the use of shared
unwrap()
results.This means:
getUsersWithDepartments
doesn’t create deep copies of user or department objects.getUsers
andgetDepartments
.getUsers
, that change is reflected anywheregetUsersWithDepartments
relied on the same underlying cache.📉 Result: Lower memory usage, less GC pressure, and zero duplication across multiple queries.
You essentially get normalization behavior without needing a normalized cache — the composition itself enforces data reuse.
🙋 What We're Asking
This pattern feels idiomatic and has been working well for us in production.
We’d love to know from the RTK Query maintainers and community:
.initiate()
in any way?.unwrap()
or cache reuse that would make this brittle?If this pattern is safe and idiomatic, maybe it’s worth promoting as a first-class technique — or even enabling in a more declarative way someday? 🤔
Thanks so much for your work — RTK Query is amazing and we’re big fans! 🙌
Beta Was this translation helpful? Give feedback.
All reactions