Hi,
I was wondering if it is possible to use apollo-cache-persist with RedwoodApolloProvider.
This would be the necessary code roughly:
import { InMemoryCache } from '@apollo/client/core';
import { persistCache, LocalStorageWrapper } from 'apollo3-cache-persist';
const cache = new InMemoryCache({...});
// await before instantiating ApolloClient, else queries might run before the cache is persisted
await persistCache({
cache,
storage: new LocalStorageWrapper(window.localStorage),
});
// Continue setting up Apollo as usual.
const client = new ApolloClient({
cache,
...
});
Unfortunately the persistCache needs to wrap InMemoryCache before the client is created. Otherwise queries will run before cache is persisted.
Therefore useApolloClient won´t suit us.
It is a bit a hen and egg case. Unfortunately the linked file also says InMemoryCache should be used within RedwoodApolloProvider.
Now we could consider another prop for the Provider, like beforeAssignCache: (cache: InMemoryCache): InMemoryCache => void. But this one would need to be async. Because the data from local storage is loaded async.
Alternatively we could add another RedwoodApolloPersistProvider. Would this make sense?
If we find a preferred way, I could go forward and try to implement it.
I have tried this, but am not fully sure if this solution works. It sometimes seems like graphql requests are called, and data is available only afterwards. Although data is persisted in cache.
const SCHEMA_VERSION = '3'// has to be updated every time the schema changes
const SCHEMA_VERSION_KEY = 'apollo-schema-version'
const CacheApp = ({ children }: PropsWithChildren) => {
const { cache } = useCache()
const [persisted, setPersisted] = useState(false)
useEffect(() => {
const currentVersion = window.localStorage.getItem(SCHEMA_VERSION_KEY)
const persistor = new CachePersistor({
cache,
storage: new LocalStorageWrapper(window.localStorage),
})
if (currentVersion === SCHEMA_VERSION) {
persistor.restore().then(() => {
setPersisted(true)
})
} else {
persistor.purge().then(() => {
setPersisted(true)
window.localStorage.setItem(SCHEMA_VERSION_KEY, SCHEMA_VERSION)
})
}
}, [])
if (persisted) {
return <>{children}</>
} else {
return <>Loading</>
}
}
....
<RedwoodApolloProvider.... >
<CacheApp>
....
</CacheApp>
</RedwoodApolloProvider>
Apollo-cache-provider definitely caches and restores data. But the App experience feels the same. If I reload, the data is queried and only after graphql finishes, the data is properly shown.
I do have the feeling, that it is related to how cells work. I haven’t looked into the code, but if the loading cell is always shown, when apollos loading value of useQuery returns true, of course no cached data will be shown.
I was not expecting this to be added in a hook like useEffect but rather just once — maybe in a layout.
May I ask why you need to persist the cache?
Are you trying to have something that detects if the user is using your app but their cached data doesn’t match the current schema? Like their app is using schema v1 and the api is now on v2?
If so this is something the team has discussed addressing — and maybe there is another way.
Also you can clear or reset the cache on logout if you want to.
I could probably also use useLayoutEffect. It is just important, that all children are only loaded after cache is restored. The recommended way is to restore cache before assigning it to apollo, but since this would only be possible within RedwoodApolloProvider, I have done it afterwards. I guess the outcome should be quite similar.
Concerning logout and reset on schema change i just followed the FAQ readme of apollo-cache-provider.
I wanted to persist cache for large tables. Since apollo does not support @stream yet, we have to query sometimes 5mb of data and that takes a while. So i wanted to keep a version in cache. In the future this might even allow offline usage.
Also to properly cache anything, Apollo client requires all object to send a typename and an id. So the table and also each row etc would need these so it can make a cache id.
Thanks a lot! I am currently investigating other issues, so I would really try the cache-first approach, maybe even cache-only, if it exists. Since this would be great for debugging. Or did you already try it?
Normally I would have thought cache-and-network should be fine.
Response caching is something i should investigate…yes.
I wouldn’t mind if data is pulled from network, as long as the cached data is shown already in the beginning. Since users won’t have to wait for data.
Pagination is an option, but only if i move all the filtering sorting etc. to the backend, and I think this might not be fun. Though it could be useful. I am much more interested though in streaming for apollog-client
It is a bit of a notion-like block structure.
It seems to work. If I load the page first with cache-and-network, switch to cache-only, the data is loaded properly. If I set cache-only and do a reload with clearing cache, it fails, since nothing is in cache of course. So I assume it all works.