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

Stale data in the with-Apollo example setup using getServerSideProps #25080

Open
ravipula opened this issue May 12, 2021 · 10 comments
Open

Stale data in the with-Apollo example setup using getServerSideProps #25080

ravipula opened this issue May 12, 2021 · 10 comments

Comments

@ravipula
Copy link

@ravipula ravipula commented May 12, 2021

What example does this report relate to?

with-Apollo

What version of Next.js are you using?

10.0.0

What version of Node.js are you using?

10.14.2

What browser are you using?

chrome

What operating system are you using?

macOS

How are you deploying your application?

Other platform

Describe the Bug

The Apollo client query response is stale when used in getServerSideProps. I think the issue is caused because of the usage of the line below this comment reference doesn't create a new client instead uses the existing one.

// For SSG and SSR always create a new Apollo Client

if (typeof window === 'undefined') return _apolloClient

Expected Behavior

Should create a new instance of the Apollo Client

To Reproduce

  1. Use Apollo client in getServerSideProps function to query data for an entity.
  2. In the client update that entity which should update the response of the query.
  3. Upon refreshing the page that is rendered with getServerSideProps. The query in the server returns stale data where as if a query is made in client it will respond with updated data.
export const getServerSideProps = async (context) => {
  const { params } = context;

  const apolloClient = initializeApollo();
  const { data } = await apolloClient.query({
    query: query,
    variables: {
      id: params.id,
    },
  });

  return addApolloState(apolloClient, {
    props: {
      item: data.item,
    },
  });
};

As in the above code block the initializeApollo function is not creating a new instance of Apollo Client instead using the existing client. Which is why stale data is being return until a rebuild is done.

If this is expected behavior, How would i be able to update the Apollo Cache on server side when the client updates an item? OR do i need to clear the cache when i make a new query in getServerSideProps?

Thank you! appreciate any help.

@NiyongaboEric
Copy link

@NiyongaboEric NiyongaboEric commented May 13, 2021

I would like to get started contributing to open source NextJS is my preferable ReactJS framework can I try this issue? How to check if the task is already taken?

@sunOpar
Copy link

@sunOpar sunOpar commented May 15, 2021

@ravipula

In the client update that entity which should update the response of the query.

Do you mean to change the apollo query in the getServerSideProps?

Upon refreshing the page that is rendered with getServerSideProps function.

Does refreshing the page means hot load refresh? If you refreshing the page proactively, it will always execute
createApolloClient because the apolloClient is undefined

@ravipula
Copy link
Author

@ravipula ravipula commented May 17, 2021

@sunOpar

Do you mean to change the apollo query in the getServerSideProps?

I mean to update that entity from the client side mutation. But the query that ran in the getServerSideProps doesn't know anything about the update that happened from client side.

Does refreshing the page means hot load refresh? If you refreshing the page proactively, it will always execute
createApolloClient because the apolloClient is undefined

It's just browser refresh and not hot load refresh. Which makes a call to the server and runs the getServerSideProps func. But at this point the earlier request to the page had executed the query and the response is stored in the cache. Even though we have updated the entity from the client mutation. This query would still return the cached response.

If it is in hot load refresh(dev mode) it would be fine. But this issue occurs when you are running it in production build.

Hope that info helps!

I have currently updated it evict the entity from the cache on every request so that if fetches info every time. This isn't an ideal solution in my opinion.

@vanpham1986

This comment was marked as spam.

@sunOpar
Copy link

@sunOpar sunOpar commented May 22, 2021

@ravipula

export async function getServerSideProps({ query }) {
  const apolloClient = initializeApollo()

  const res = await apolloClient.query({
    query: gql`
      query GetAllPosts {
        allPosts {
          author
        }
      }
    `,
  })

  return addApolloState(apolloClient, {
    props: { data: res.data },
  })
}

What I did are:

  1. open browser and query data from getServerSideProps
  2. change the server-side entities(add more allPosts entities etc.)
  3. refresh the page

after that, I got the latest value from getServerSideProps query func.
I think i miss something to reproduce the issue, could you help me?

@ravipula
Copy link
Author

@ravipula ravipula commented May 22, 2021

@sunOpar

I think i miss something to reproduce the issue, could you help me?

I think the apollo client is caching your query response but it is probably fetching new items since the query you are making doesn't really have an identifier to it. It is more prominent if you were actually querying a single item instead. For eg:

export async function getServerSideProps({ query }) {
  const apolloClient = initializeApollo()

  const res = await apolloClient.query({
    query: gql`
      query Post {
        Post {
            id
            author
        }
      }
    `,
  })

  return addApolloState(apolloClient, {
    props: { data: res.data },
  })
}

Try something like this, you'd be able to reproduce it. You might know but just as an FYI the id in the item is needed here as Apollo Client uses that and typename as an identifier to store in the cache. If at all there is no id then you'd have to specify a type policy to use some other value as an identifier.

Hope this helps and thank you for taking time to reproduce this.

@eugle
Copy link

@eugle eugle commented May 31, 2021

How did you solve the same problem that keeps bothering me?

The default policy of Apollo Client should be cache first. It will query data from the cache first. If your data has been updated but the ID in the cache is the same, it will not fetch data from the server again

The first solution is:
Attempted to change the policy, using fetchPolicy = 'network-only ', which would lose the advantage of Apollo, fetching data from the server every time, no caching, and slower pages

The second solution is:
If the query does not query in getServersideProps, but instead in the page component, it can retrieve the latest data, but in this way, it will lose the SSR and cause the SEO to fail

I tried to change the policy to cache-and-network in getServersideProps, but Apollo gave an error and could only use cache-first or network-only

How can we solve this problem so that we can get the latest data and make the SSR effective?

In addition, I tried the method of clearing the cache after changing the data, but it didn't work, like this

export default async function resetStore() {
    await client.resetStore();
    await client.clearStore();
    cache.gc();
}

When I tried to clean it up, I ran this function, and strangely it didn't work,It's not the same origin policy issue, I'm doing it on the same domain name

@ravipula
Copy link
Author

@ravipula ravipula commented Jun 1, 2021

@eugle

My code looks like this

export const getServerSideProps = async (context) => {
  const { params } = context;

  // Initialize Apollo
  const apolloClient = initializeApollo();

  // Get the Author from cache
  const identifier = apolloClient.cache.identify({
    __typename: 'Author',
    id: params.id,
  });

  // Evict from cache and
  apolloClient.cache.evict({ id: identifier });
  apolloClient.cache.gc();

  // The query runs here.
}

The issues with this implementation is that it will get the fresh data every single time the request is triggered to this page. You will have SSR but the item is cleared from the cache and added to the cache though it won't affect other items in the cache. I wasn't able to come up with a better solution for this as of now. Hope that helps.

@eugle
Copy link

@eugle eugle commented Jun 2, 2021

@ravipula Thank you for the code example. Have you ever thought about using subscriptions for this purpose

@ravipula
Copy link
Author

@ravipula ravipula commented Jun 3, 2021

@eugle

Thank you for the code example. Have you ever thought about using subscriptions for this purpose

I am actually not all that familiar with subscriptions and haven't thought about using since the issue is somewhat resolved for now. I'm not how that will fit into this setup though with all the inclusions of websockets and the apolloclient being on the server itself in this instance. Have to think in through.

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

Successfully merging a pull request may close this issue.

None yet
6 participants