Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use with universal/isomorphic apps (NextJS) #32

Closed
anselmdk opened this issue Feb 9, 2018 · 28 comments
Closed

How to use with universal/isomorphic apps (NextJS) #32

anselmdk opened this issue Feb 9, 2018 · 28 comments
Labels

Comments

@anselmdk
Copy link

anselmdk commented Feb 9, 2018

I've been playing around a little getting this to work with Nextjs and localStorage, specifically with the with-apollo example, with no luck.
The gist seems to be that the example initialises apollo twice. First on the server, and subsequently on the client, with the initial state set on the server. On the server, however localStorage is not available, hence I initialized on the client only, which seems to be the issue (localStorage is written to, but not rehydrated from):

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

const cache = new InMemoryCache({...});

if (process.browser) {
  persistCache({
    cache,
    storage: window.localStorage,
  });
}

The question is, will this potentially never work in an universal app like NextJS, and what would be the alternatives? The README mentions that redux-persist-cookie-storage (which seems to cater for universal apps) could be used interchangeably, but I've had no luck using that either.

@jamesreggio
Copy link
Contributor

Hi @anselmdk

Unfortunately, apollo-cache-persist doesn't work well with server-side rendering.

If you're using server-side rendering to prefetch the Apollo queries needed for the page (per the guide here), you won't be able to use apollo-cache-persist.

The reason for this is that, at least right now, it's not possible to merge two separate copies of the store. With server-side rendering, you're essentially filling a store on the server, and then persisting it to the page and restoring it in the browser. apollo-cache-persist is also storing and loading a copy of the store, and there's no way to merge the two copies — whichever one loads last will win.

In general, the value of apollo-cache-persist is greatly diminished if you're using server-side rendering, since apollo-cache-persist is primarily focused upon reducing initial load times — e.g., one of the same goals as server-side rendering.

In Apollo Client 3.0, we'll think about a better way to coordinate these two technologies.

I hope this helps!

@anselmdk
Copy link
Author

anselmdk commented Mar 7, 2018

Hi @jamesreggio, thanks for the answer. This was a little bit my fear.
The use case for using cache persist for us was to have the apollo link (client) state persist.
For now we're using a mix of link state and cookies to solve this problem for us, which is not exactly pretty.
I'm looking forward to what you come up with in Apollo Client 3.0, as the popularity of NextJS is rising, and I think our use case is a pretty common one, which was fairly well supported in the Redux days.

@majelbstoat
Copy link

@anselmdk you probably figured out this workaround already, but faced with a similar situation, I restore all my client side state in the getInitialProps of the withData HOC, using writeQuery directly into the apollo client. Not pretty, but it works. I too, look forward to a better integrated solution!

@ghost
Copy link

ghost commented Oct 5, 2018

@majelbstoat I am facing the same issue. Can you provide a snippet of your workaround implementation?

@majelbstoat
Copy link

majelbstoat commented Oct 5, 2018

@mshahov:

    static async getInitialProps(ctx: InitialContext<any>): Promise<WithPageProps> {
      const { req, res, asPath, pathname, query } = ctx

      let token
      if (req) {
        const { session } = req
        if (session) {
          token = session.token
        }
      }

      const apollo = createApolloClient(null, token)
      if (req) {
        const { session, env } = req

        // Write initial values for local client state
        // TODO(jamie) Figure out how to not have to specify these here.
        apollo.writeQuery({
          query: SESSION,
          data: { session },
        })
        apollo.writeQuery({
          query: ENV,
          data: { env },
        })
        apollo.writeQuery({
          query: SAVE_STATUS,
          data: { saveStatus: 'WAITING' },
        })
      }

      // This seems strange, but we are reading this on both client and server.
      const result: session | null = apollo.readQuery({
        query: SESSION,
      })

      const session = result ? result.session : null

     // ... rest of the stuff in getInitialProps, quite a lot like the with-apollo example in nextjs.

It's kinda ugly, but does the job. I'm kind of unwilling to expand the use of client/apollo-link-state stuff until this gets better though.

@lucasconstantino
Copy link

lucasconstantino commented Oct 29, 2018

I'm trying to figure out a lib to perform a more granular cache persisting system. My tests so far show that SSR and client-side persistence could live well together. https://github.com/TallerWebSolutions/apollo-cache-instorage

@Enalmada
Copy link

Enalmada commented Nov 30, 2018

at least right now, it's not possible to merge two separate copies of the store

Can apollo-cache-persist consider doing what TallerWebSolutions/apollo-cache-instorage is doing:

When restoring the cache (SSR hydration, for instance), keep in mind that any value inserted via hydrating will have precedence over the persisted data.

SSR helps but when web users come back to the site or open another tab, it would be ideal to not refetch all the data that has already been fetched.

@luandro
Copy link

luandro commented Mar 13, 2019

Any updates on how to do this?

@tafelito
Copy link

@anselmdk can you share what was your solution to deal with this issue. I'm doing something similar to what you were doing, just trying to use the apollo-cache-persists only for client side data, so when I refresh the browser I can still see the data without having to store it on the server.

@anselmdk
Copy link
Author

anselmdk commented Jul 1, 2019

Guys, sorry for not hearing your mentions. We went away from apollo-cache-persist because of the mentioned limitations, and are now persisting our sessions on the server.

@vitaliemiron
Copy link

Did you find any solutions for this ?

@wilmervanheerde
Copy link

Is support going to be implemented any time soon? There's another package called @wora/apollo-offline that manages to achieve this. Looking at their source it doesn't seem too hard to achieve

@sanderkooger
Copy link

sanderkooger commented Aug 19, 2020

@wilmervanheerde My fellow (Dutchie) i presume. Thank you for showing me @wora/apollo-offline. But if im not mistaken its not running with AC3.

If you have any idea how to persist AC# with local state in an SSR app we could really use some advice. IF you can help we will gladly opensource a boilerplate so others can implement it in nextjs as well.

@wtrocki
Copy link
Collaborator

wtrocki commented Aug 19, 2020

AC#

ROFL :)

How about disabling persist if window is undefined?
Many people get that with success

@sanderkooger
Copy link

AC#

ROFL :)

How about disabling persist if window is undefined?
Many people get that with success

My bad typo... Still though, what is the best way to persist cache in an SSR application @wtrocki ? We are working with a team of super enthusiast but very junior devs to build a streaming platform for fashion students. And persisting cache is a good way is one of our issues. We are willing to opensource the boilerplate for this so others can do the same. Would you be willing to help us out?

@sh0umik
Copy link

sh0umik commented Aug 31, 2020

Any progress on this? We failed to implement this with Next.js Project.

@sanderkooger
Copy link

Yes I have made a working example.

Important sidenote it only caches the apollo cache not the reactive variables.

import { useMemo } from 'react';
import { ApolloClient, HttpLink } from '@apollo/client';
import { persistCache } from 'apollo-cache-persist';

import cache from './cache';



if (typeof window !== 'undefined') {
  try {
    // See above for additional options, including other storage providers.
     persistCache({
      cache,
      storage: window.localStorage,
    });
  } catch (error) {
    console.error('Error restoring Apollo cache', error);
  }
}
let apolloClient;
function createApolloClient() {
  return  new ApolloClient({
    cache,
    ssrMode: typeof window === 'undefined',
    link: new HttpLink({
      uri: 'https://tifgraphql.azurewebsites.net/v1/graphql',
      // uri: 'https://nextjs-graphql-with-prisma-simple.vercel.app/api', // Server URL (must be absolute)
      credentials: 'same-origin' // Additional fetch() options like `credentials` or `headers`
    })
  });
}

export function initializeApollo(initialState = null) {
  // eslint-disable-next-line no-underscore-dangle
  const _apolloClient = apolloClient ?? createApolloClient();

  // If your page has Next.js data fetching methods that use Apollo Client, the initial state
  // gets hydrated here
  if (initialState) {
    // Get existing cache, loaded during client side data fetching
    const existingCache = _apolloClient.extract();
    // Restore the cache using the data passed from getStaticProps/getServerSideProps
    // combined with the existing cached data
    _apolloClient.cache.restore({ ...existingCache, ...initialState });
  }
  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return _apolloClient;
  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient;

  return _apolloClient;
}

export function useApollo(initialState) {
  const store = useMemo(() => initializeApollo(initialState), [initialState]);
  return store;
}

@sh0umik
Copy link

sh0umik commented Aug 31, 2020

What is this line do ? import cache from './cache';

@sanderkooger
Copy link

sanderkooger commented Sep 1, 2020

What is this line do ? import cache from './cache';

It imports the cache from a file its specified in.

@krish-dev
Copy link

@sanderkooger I tried your example but it's not working with Apollo 3. Do you have any example repo?

@sanderkooger
Copy link

sanderkooger commented Apr 4, 2021

@krish-dev , no not yet, I am looking for a solution though, If you find one before me let me know please

@RishikeshDarandale
Copy link

Anyone found solution?

@j-lee8
Copy link

j-lee8 commented Aug 19, 2021

So the Apollo cache can't be persisted with Nextjs? Back to Redux it is...

@sanderkooger
Copy link

@JavaJamie we actually managed to get it working! @onair-lena could you share how its fixed for us ?

@RishikeshDarandale
Copy link

@onair-lena, can you please share with us how you have used it?

@adrianos10
Copy link

adrianos10 commented Oct 14, 2021

I think I've managed to add apollo-cache-persist based on with-apollo example from Next.js repo - vercel/next.js#29718

@besSejrani
Copy link

@adrianos10 Do you know how much time it will take before the author accepts your PR ?

@adrianos10
Copy link

@besSejrani No idea, I will ping them. But this is just an example based on existing with-apollo example, so you can check if it works for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests