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

Handling Apollo Client Cache Hydration in Next.js 13's app directory #10466

Closed
ProchaLu opened this issue Jan 23, 2023 · 6 comments
Closed

Handling Apollo Client Cache Hydration in Next.js 13's app directory #10466

ProchaLu opened this issue Jan 23, 2023 · 6 comments

Comments

@ProchaLu
Copy link

ProchaLu commented Jan 23, 2023

I have a question regarding the Cache Hydration in Next.js 13 since they introduced app/ directory + React server components.

What is the purpose of the apolloClient.cache.restore() and apolloClient.extract() methods in the context of using Apollo Client with Next.js 13's app directory, and is it necessary to use this to get the existing cache loaded during client-side data fetching and restore the cache with the merged data?

function initializeApollo(initialState: any = null) {
  const internalApolloClient = _apolloClient ?? createApolloClient();

  // If your page has Next.js data fetching methods that use Apollo Client, the initial state
  // get hydrated here
  if (initialState) {
    // Get existing cache, loaded during client side data fetching
    const existingCache = internalApolloClient.extract();

    // Merge the existing cache into data passed from getStaticProps/getServerSideProps
    const data = merge(initialState, existingCache);

    // Restore the cache with the merged data
    internalApolloClient.cache.restore(data);
  }
  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return internalApolloClient;
  // Create the Apollo Client once in the client
  if (!_apolloClient) _apolloClient = internalApolloClient;

  return internalApolloClient;
}

Here is a link to the Next.js 13 example:
https://github.com/vercel/next.js/blob/84720697e4f00bd77689153f7cf39421be9b1d25/ex[…]mples/api-routes-apollo-server-and-client-auth/apollo/client.js

@ProchaLu ProchaLu changed the title Handling Apollo Client Cache Hydration in Next.js 13 Handling Apollo Client Cache Hydration in Next.js 13's app directory Jan 23, 2023
@Josehower
Copy link

Josehower commented Feb 9, 2023

With @ProchaLu we managed to get the queries done server-side being cached in the frontend, passing the cache to a client component using ApolloProvider

You can find an example here: https://stackblitz.com/github/Josehower/next-js-13-with-apollo-cache?file=util%2Fclient.ts

// app/page.js => Server Component
export default async function HomePage() {
  const client = initializeApollo(null);

  await client.query({
    query: gql`
      // ... query logic
    `,
  });

  return (
    <main>
      <ApolloClientProvider 
        initialApolloState={JSON.stringify(client.cache.extract())} // Extract the cache from the server client
      >
        <Animals />
      </ApolloClientProvider>
    </main>
  );
}
// app/ApolloClientProvider.tsx => Client Component

type Props = {
  initialApolloState: string;
  children: React.ReactNode;
};

export default function ApolloClientProvider(props: Props) {
  const apolloClient = useApollo(JSON.parse(props.initialApolloState)); // initialize client with the server cache 
  return (
    <ApolloProvider client={apolloClient}>{props.children}</ApolloProvider>
  );
}

The way we manage to get the clients synchronised was by extracting the cache server-side and using it to initialize the frontend-side client

// used first time on the server with null
export function initializeApollo(
  initialState: NormalizedCacheObject | null = null,
) {
  const _apolloClient = apolloClient ?? createApolloClient();

  // If your page has Next.js data fetching methods that use Apollo Client, the initial state
  // get hydrated here
  if (initialState) {
    _apolloClient.cache.restore(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;
}

// used on ApolloClientProvider Component with the cache extracted on the HomePage Server Component
export function useApollo(initialState: NormalizedCacheObject) {
  const store = useMemo(() => initializeApollo(initialState), [initialState]);
  return store;
}

There are still things that can be better, and this may need some adaptations to apollo-client #10344 (comment)

Unfortunately, not being able to use context on a Server Component is definitely something making our life hard.

Similar issues impact Emotion:

Other libraries using context also have this issue, eg. emotion-js/emotion#2928, where emotion-js/emotion#2978.

@jetaggart
Copy link

Hey @Josehower, if I'm reading that code correctly you have to embed your custom ApolloProvider into every page?

@Josehower
Copy link

sorry for the late response i was on vacations.

Yes every page that need access to the Apollo Client need to use this provider component. I know is a bit annoying but in order to use the Apollo provider you need to have a Client Component.

An alternative option would be add the provider on a layout but that means from there all the pages should be client components what is also not nice.

Unfortunately this is something that probably need to be addressed by Apollo since the lack of useContext on Server Components has this limitation.

@karlhorky
Copy link

karlhorky commented Feb 27, 2023

@Gasheck posted an alternative solution in the main Next.js 13 app directory issue (not sure yet what the downsides are, if there are any):

@phryneas
Copy link
Member

Hey everyone, I just wanted to let you all know that we released a package to support React Server Components and the new streaming SSR for Client Components that the Next.js App router offers.

You can find the package repository over here: @apollo/experimental-nextjs-app-support
Here is an introductory blog post: Using Apollo Client with Next.js 13: releasing an official library to support the App Router
And if you want to dive deep in the tech, and why we made certain limitations, there is a very long RFC with a discussion going on in RFC: The Next.js "App Router", React Server Component & "SSR with Suspense" story

I will be closing this issue here and would love it if you could give us feedback and suggestions for improvements over in the other repo!

@github-actions
Copy link
Contributor

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants