Search code examples
reactjsgraphqlnext.jsapolloapollo-client

Apollo Client cache doesn't update on query when variables are changing


I'm totally new to Next, GraphQL and Apollo. I'm developing a twitter-clone thing for practice.

The index page displays the user feed with all the new posts. I'm executing a query that returns an object "PaginatedPosts" that contains the posts and a boolean, if there are more posts to fetch (to decide if the "load more posts" button should be visible or not).

If you click on the creator (user) of the post you get to a page (ex: /user/8) that has the user's profile and all his posts. I'm server side loading this page, for better SEO. I'm getting the user id from the url, then I make a query to get user and then I make another query, to get his posts, that includes a variable with the his id.

Now my problem, if I go to a user's profile, the query gets cached by apollo, cool. When I visit another user's profile, the posts that are shown to me are from the previous user (from the cache). I tried adding fetch-policy: no-cache but then if I like a post, cache doesn't update automatically. It seems weird though because the user, that gets display at the top of the user profile page, changes normally.

Here is the code I'm using on the front-end user page:

const {
    data: postsData,
    loading: postsLoading,
    fetchMore: fetchMorePosts,
    variables: postsVariables,
} = useUserPostsQuery({
    notifyOnNetworkStatusChange: true,
    skip: !userData?.user, // skip if no user id
    variables: {
      limit: 15,
      userId: userData?.user?.id || -1,
    },
});

Create apollo client function:

const createApolloClient = (ctx: any) => {
  let cookie = "";
  if (isServer() && ctx) {
    cookie = ctx.req.headers.cookie;
  }

  return new ApolloClient({
    uri: "http://localhost:4000/graphql",
    credentials: "include",
    headers: {
      cookie,
    },
    cache: new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            posts: {
              keyArgs: [],
              merge(
                existing: PaginatedPosts | undefined,
                incoming: PaginatedPosts
              ): PaginatedPosts {
                return {
                  ...incoming,
                  posts: [...(existing?.posts || []), ...incoming.posts],
                };
              },
            },
            userPosts: {
              keyArgs: [],
              merge(
                existing: PaginatedPosts | undefined,
                incoming: PaginatedPosts
              ): PaginatedPosts {
                return {
                  ...incoming,
                  posts: [...(existing?.posts || []), ...incoming.posts],
                };
              },
            },
          },
        },
      },
    }),
  });
};

Let me know if you need additional information.


Solution

  • I ended up adding userId to keyArgs (in create apollo client function). Like so:

    const createApolloClient = (ctx: any) => {
      let cookie = "";
      if (isServer() && ctx) {
        cookie = ctx.req.headers.cookie;
      }
    
      return new ApolloClient({
        uri: "http://localhost:4000/graphql",
        credentials: "include",
        headers: {
          cookie,
        },
        cache: new InMemoryCache({
          typePolicies: {
            Query: {
              fields: {
                posts: {
                  keyArgs: [],
                  merge(
                    existing: PaginatedPosts | undefined,
                    incoming: PaginatedPosts
                  ): PaginatedPosts {
                    return {
                      ...incoming,
                      posts: [...(existing?.posts || []), ...incoming.posts],
                    };
                  },
                },
                userPosts: {
                  keyArgs: ["userId"], // right there
                  merge(
                    existing: PaginatedPosts | undefined,
                    incoming: PaginatedPosts
                  ): PaginatedPosts {
                    return {
                      ...incoming,
                      posts: [...(existing?.posts || []), ...incoming.posts],
                    };
                  },
                },
              },
            },
          },
        }),
      });
    };