apollo icon indicating copy to clipboard operation
apollo copied to clipboard

Support apollo-cache-persist

Open gotenxds opened this issue 5 years ago • 14 comments

What problem does this feature solve?

Saving data locally to local storage is imperative for PWA, this is supported by apollo-cache-persist but currently it is unclear/impossible to support it due to the client config not being loaded at the client (mode: client) and thus the window and subsequently the local storage is unavailable.

What does the proposed changes look like?

Ultimately this will be supported internally to support [loading before rendeing] : https://github.com/apollographql/apollo-cache-persist#how-do-i-wait-for-the-cache-to-be-restored-before-rendering-my-app with an added config option for which store to use.

alternatively allowing to run the apollo config in the client might solve this.

This feature request is available on Nuxt community (#c254)

gotenxds avatar Oct 28 '19 23:10 gotenxds

I haven't try apollo-cache-persist before. apollo-module is simply embed of vue-cli-plugin-apollo to create vue-apollo client with some more options use for vue-apollo. You can take a look of all options, on README file or here https://vue-cli-plugin-apollo.netlify.com/guide/configuration.html#createapolloclient-options

It's have a cache option so you can try, may be it's look like this:

nuxt.config.js

{
  modules: ['@nuxtjs/apollo'],
  apollo: {
    clientConfigs: {
      default: '@/apollo-config.js'
    }
  }
}

in apollo-config.js

import { InMemoryCache } from 'apollo-cache-inmemory'
import { persistCache } from 'apollo-cache-persist'

export default function (ctx) {
  const cache = new InMemoryCache()
  try {
    // See above for additional options, including other storage providers.
    persistCache({
      cache,
      storage: window.localStorage
    })
  } catch (error) {
    console.error('Error restoring Apollo cache', error)
  }
  return {
    httpEndpoint: YOUR_ENDPOINT,
    cache
  }
}

not sure if it's work. Also I found this issue, it could be relevant: https://github.com/vuejs/vue-apollo/issues/565

kieusonlam avatar Oct 29 '19 15:10 kieusonlam

@kieusonlam hi, thanks for the answer, I already tried exactly your code, the issue is that when the config is being run by next the window once is not present so you can't access the local storage

gotenxds avatar Oct 29 '19 16:10 gotenxds

Hi @gotenxds - did you ever find a solution for this?

fresh5447 avatar Jan 24 '20 19:01 fresh5447

I believe the solution is to write "node safe" code

  if (typeof window === 'object') {
    persistCache({
      cache,
      storage: window.localStorage
    });
  }

@gotenxds / @kieusonlam

fresh5447 avatar Jan 28 '20 19:01 fresh5447

@fresh5447 have you tried it?

kieusonlam avatar Feb 04 '20 01:02 kieusonlam

I just tried this, but it unfortunately does not work. Since the window is not available the persistCache does not get initialized which causes multiple errors when the site is loaded.

RubenVanEldik avatar Jul 07 '20 13:07 RubenVanEldik

Hya @kieusonlam @RubenVanEldik @fresh5447 Sorry for the long wait I did not notice you asked, I did find a "solution" for this, tell me what you think

In my config.ts file I import a file I call getCache and use that in as the cache:

import cache from './getCache'

export default (ctx: Context) => {
  const onCacheInit = (cache: InMemoryCache) => {
    const data = {
...
    }
    cache.writeData({ data })
  }

  return {
    cache,
...
  }
}

in getCache file looks like this:

import { InMemoryCache } from 'apollo-cache-inmemory'

const cache = new InMemoryCache({
  freezeResults: true,
})

export default cache

Now I also made a plugin file in /plugins/persistCache.ts

import Vue from 'vue'
import { persistCache } from 'apollo-cache-persist'
import cache from '../apollo/getCache'

export default () => {
  persistCache({
    cache,
    // debug: true,
    storage: window.localStorage as any,
    maxSize: false,
  }).then(() => {
    Vue.prototype.$cacheReady = true
  })
}

and in the config:

  plugins: [
    { src: '~/plugins/persistCache', mode: 'client' },
  ],

Hope this helps

gotenxds avatar Jul 11 '20 17:07 gotenxds

Hi @gotenxds,

Thank you for your time and attention!

I didn't get it to work yet, but it might be because I don't understand two parts of your code yet.

  1. What does this do? a. What do you have to put in the data const? b. Is cache: InMemoryCache valid syntax?
  const onCacheInit = (cache: InMemoryCache) => {
    const data = {
...
    }
    cache.writeData({ data })
  }
  1. Is as any a typo? My linter throws an error and I don't know this syntax as well. 🙃
storage: window.localStorage as any,

RubenVanEldik avatar Jul 17 '20 12:07 RubenVanEldik

Hi @gotenxds,

Thank you for your time and attention!

I didn't get it to work yet, but it might be because I don't understand two parts of your code yet.

  1. What does this do? a. What do you have to put in the data const? b. Is cache: InMemoryCache valid syntax?
  const onCacheInit = (cache: InMemoryCache) => {
    const data = {
...
    }
    cache.writeData({ data })
  }
  1. Is as any a typo? My linter throws an error and I don't know this syntax as well. 🙃
storage: window.localStorage as any,

Hi @RubenVanEldik This is just typescript syntax, if you're using plain javascript you can juse remove that, so it will look like this:

  const onCacheInit = (cache) => {
    const data = {
...
    }
    cache.writeData({ data })
  }
export default () => {
  persistCache({
    cache,
    // debug: true,
    storage: window.localStorage,
    maxSize: false,
  }).then(() => {
    Vue.prototype.$cacheReady = true
  })
}

gotenxds avatar Jul 18 '20 09:07 gotenxds

You can wrap it within process.client like so... the Nuxt.js way.

import { persistCache } from "apollo-cache-persist";

if (process.client) {
  try {
    persistCache({
      cache,
      storage: window.localStorage,
      key: "your-custom-cache-key",
    })
  } catch (error) {
    console.error('Error restoring Apollo cache', error)
  }
};

szvest avatar Jul 19 '20 23:07 szvest

I am stuck at this too. Any plans to implement this?

mckraemer avatar Jun 13 '21 01:06 mckraemer

Vue.prototype.$cacheReady

@gotenxds I am curious as to what you do with this Vue.prototype.$cacheReady value, I suppose you wait for it to be true somewhere before the first render... But where? Anywhere I try, persistCache always finishes after client-side rendering begins and triggers issues...

SylvainBigonneau avatar Feb 19 '22 12:02 SylvainBigonneau

For some one who may need in furure, I tested it and it okay but still has cors error

import { createHttpLink, from } from '@apollo/client/core';
import { provideApolloClient } from '@vue/apollo-composable';
import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries';
import { sha256 } from 'crypto-hash';

export default defineNuxtPlugin((nuxtApp) => {
  const { $apollo } = nuxtApp;

  const linkChain = createPersistedQueryLink({ sha256 });
  
  $apollo.defaultClient.setLink(from([linkChain]));
  provideApolloClient($apollo.defaultClient);
});

vuthanhnhan avatar Jan 24 '24 09:01 vuthanhnhan

For some one who may need in furure, I tested it and it okay but still has cors error

import { createHttpLink, from } from '@apollo/client/core';
import { provideApolloClient } from '@vue/apollo-composable';
import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries';
import { sha256 } from 'crypto-hash';

export default defineNuxtPlugin((nuxtApp) => {
  const { $apollo } = nuxtApp;

  const linkChain = createPersistedQueryLink({ sha256 });
  
  $apollo.defaultClient.setLink(from([linkChain]));
  provideApolloClient($apollo.defaultClient);
});

I think that is a different issue, persistent queries as compared to persistcache, but maybe we can do something similar

dpmillerau avatar Feb 15 '24 03:02 dpmillerau