Hey everyone,
I’m planning another major overhaul of this project to address some long-standing issues, and I’d love to get feedback before committing to a design.
⸻
Motivation
Supabase Cache Helpers does a lot of magic under the hood. Most users I’ve talked to don’t even realize how much is happening behind the scenes.
That’s been okay for me personally—I can always jump in and reason about the quirks—but for others it leads to a loss of trust. And unnecessarily so, because most users don’t actually expect or need half of this magic when they adopt the library.
The goal of this iteration is to:
• remove hidden magic
• narrow the scope
• define clear expectations
⸻
Status Quo
Right now, Cache Helpers tries to do everything:
• automatic cache key generation
• pagination & infinite scroll
• mutations
• auto-populating and updating the cache
You can read more about the current behaviour here.
With all these features combined, Cache Helpers ends up very close to an offline-first framework—without actually being one.
Surprisingly, it works really well. In our production app, we’ve barely had to write custom cache updates over the past years. Still, this comes at the cost of a large number of weird solutions and edge-case fixes accumulated over time.
⸻
The Core Problems
1. Auto-expansion of mutations
The most prominent piece of magic is auto-expansion:
Before a mutation is executed, all cache keys are scanned for queries on the same table, and the .select() statement is expanded to include all paths that any query selects or filters on.
This works well most of the time—but it can also lead to confusing behavior. A single expensive !inner join added somewhere else in the app can suddenly affect mutations in an entirely different area.
From a developer’s perspective, this raises questions like:
Why is my mutation querying data that I never asked for?
Yes, you can disable this via disableAutoQuery, but by that point you’re already dealing with a large, implicit dependency graph across the codebase.
⸻
2. Offline cache updates
After a mutation succeeds, Cache Helpers attempts to:
• iterate over every cached query
• re-apply filters offline by mimicking Postgres behavior in the browser
• update cached data in place if the row matches
This requires solving a long list of hard problems:
• inserting an item at the correct index in an ordered list
• moving items when updates change ordering
• marshalling/unmarshalling internal data structures of infinite-scroll hooks
(which are not stable public APIs)
• ensuring items remain valid members of a query after updates
• supporting every PostgREST operator and keeping up with new ones
• correctly handling joins vs jsonb columns (e.g. deep merge vs replace)
…and more.
At this point, we’re essentially band-aiding the hardest problems of offline-first frameworks, without fully committing to being one.
⸻
A Way Forward
Recent years in web development have shown one thing very clearly: offline-first is extremely hard. (We even got Postgres running in the browser!)
Every solution that has stood the test of time relies on client-side incremental view maintenance (IVM). TanStack DB is emerging as the default library in that space.
For the next major version, I propose:
Removing all “offline” cache mutation magic from the SWR and TanStack Query adapters, and instead focusing on smart revalidation.
If full offline support is desired, we could build a TanStack DB adapter using the shared primitives of Cache Helpers.
⸻
What This Means in Practice
• Cached data is never mutated offline
• Offline filters are only used to decide whether revalidation is necessary
• Queries may be temporarily stale, but will always be correct
This brings several benefits:
• Auto-expansion is reduced to only the data needed for filtering, not everything queried anywhere in the app
• Adapters rely solely on public APIs of cache libraries → lower maintenance cost and easier external contributions
• Opens the door for a TanStack DB adapter with real offline support
• Much clearer mental model for users
⸻
Feedback & Expectations
This change can have a significant impact on existing codebases, so I want to gather feedback before starting the work. My hope is that the overall response will be positive, but I want to hear concerns early.
It’s also important to note that I will not be providing a TanStack DB adapter myself, since offline support is not important for me right now. But I am hoping contributors will pick this up once the new release is out, and I am very happy support in every way I can.
Looking forward to your thoughts, concerns, and ideas.
Hey everyone,
I’m planning another major overhaul of this project to address some long-standing issues, and I’d love to get feedback before committing to a design.
⸻
Motivation
Supabase Cache Helpers does a lot of magic under the hood. Most users I’ve talked to don’t even realize how much is happening behind the scenes.
That’s been okay for me personally—I can always jump in and reason about the quirks—but for others it leads to a loss of trust. And unnecessarily so, because most users don’t actually expect or need half of this magic when they adopt the library.
The goal of this iteration is to:
• remove hidden magic
• narrow the scope
• define clear expectations
⸻
Status Quo
Right now, Cache Helpers tries to do everything:
• automatic cache key generation
• pagination & infinite scroll
• mutations
• auto-populating and updating the cache
You can read more about the current behaviour here.
With all these features combined, Cache Helpers ends up very close to an offline-first framework—without actually being one.
Surprisingly, it works really well. In our production app, we’ve barely had to write custom cache updates over the past years. Still, this comes at the cost of a large number of weird solutions and edge-case fixes accumulated over time.
⸻
The Core Problems
1. Auto-expansion of mutations
The most prominent piece of magic is auto-expansion:
Before a mutation is executed, all cache keys are scanned for queries on the same table, and the .select() statement is expanded to include all paths that any query selects or filters on.
This works well most of the time—but it can also lead to confusing behavior. A single expensive !inner join added somewhere else in the app can suddenly affect mutations in an entirely different area.
From a developer’s perspective, this raises questions like:
Yes, you can disable this via
disableAutoQuery, but by that point you’re already dealing with a large, implicit dependency graph across the codebase.⸻
2. Offline cache updates
After a mutation succeeds, Cache Helpers attempts to:
• iterate over every cached query
• re-apply filters offline by mimicking Postgres behavior in the browser
• update cached data in place if the row matches
This requires solving a long list of hard problems:
• inserting an item at the correct index in an ordered list
• moving items when updates change ordering
• marshalling/unmarshalling internal data structures of infinite-scroll hooks
(which are not stable public APIs)
• ensuring items remain valid members of a query after updates
• supporting every PostgREST operator and keeping up with new ones
• correctly handling joins vs jsonb columns (e.g. deep merge vs replace)
…and more.
At this point, we’re essentially band-aiding the hardest problems of offline-first frameworks, without fully committing to being one.
⸻
A Way Forward
Recent years in web development have shown one thing very clearly: offline-first is extremely hard. (We even got Postgres running in the browser!)
Every solution that has stood the test of time relies on client-side incremental view maintenance (IVM). TanStack DB is emerging as the default library in that space.
For the next major version, I propose:
If full offline support is desired, we could build a TanStack DB adapter using the shared primitives of Cache Helpers.
⸻
What This Means in Practice
• Cached data is never mutated offline
• Offline filters are only used to decide whether revalidation is necessary
• Queries may be temporarily stale, but will always be correct
This brings several benefits:
• Auto-expansion is reduced to only the data needed for filtering, not everything queried anywhere in the app
• Adapters rely solely on public APIs of cache libraries → lower maintenance cost and easier external contributions
• Opens the door for a TanStack DB adapter with real offline support
• Much clearer mental model for users
⸻
Feedback & Expectations
This change can have a significant impact on existing codebases, so I want to gather feedback before starting the work. My hope is that the overall response will be positive, but I want to hear concerns early.
It’s also important to note that I will not be providing a TanStack DB adapter myself, since offline support is not important for me right now. But I am hoping contributors will pick this up once the new release is out, and I am very happy support in every way I can.
Looking forward to your thoughts, concerns, and ideas.