apollographql / apollo-client-nextjs

Apollo Client support for the Next.js App Router
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support
MIT License
358 stars 25 forks source link

How does hydration work #154

Closed jsindos closed 4 months ago

jsindos commented 5 months ago

I am successfully consuming my graphql endpoint on the server with the code below. However, I can't seem to access the local apollo cache with window.__APOLLO_CLIENT__.cache.extract(). I am wondering how client hydration works with this library?

'use client'
import { useSuspenseQuery } from '@apollo/experimental-nextjs-app-support/ssr'
import { gql } from '@apollo/client'
import React from 'react'

const TEST_QUERY = gql`
  query Test {
    test
  }
`

export default function Home () {
  const { data } = useSuspenseQuery(TEST_QUERY)

  return (
    <React.Suspense fallback={'Loading...'}>
      <ErrorBoundary>
        <div>
          Test query result: {data.test}
        </div>
      </ErrorBoundary>
    </React.Suspense>
  )
}

function ErrorBoundary ({ children }) {
  // This is a simple implementation of error boundary.
  // In a real-world application, you might want to use a more sophisticated error boundary.
  return children
}
phryneas commented 5 months ago

It works automatically piece by piece for every Query that resolves during SSR. You don't have to do anything manually (apart from the initial setup with NextSSRApolloClient, NextSSRInMemoryCache and ApolloNextAppProvider).

jsindos commented 5 months ago

@phryneas I've directly copied the example from the docs for my component as seen below. There is no network request for /graphql so SSR is working as intended.

image

However, I get the following error when trying to access the cache, is there another way to access the cache object through the browser console?

image
'use client'
// ^ this file needs the "use client" pragma

import { ApolloLink, HttpLink } from '@apollo/client'
import {
  ApolloNextAppProvider,
  NextSSRInMemoryCache,
  NextSSRApolloClient,
  SSRMultipartLink
} from '@apollo/experimental-nextjs-app-support/ssr'

// have a function to create a client for you
function makeClient () {
  const httpLink = new HttpLink({
    // this needs to be an absolute url, as relative urls cannot be used in SSR
    uri: 'http://localhost:8080/graphql',
    // you can disable result caching here if you want to
    // (this does not work if you are rendering your page with `export const dynamic = "force-static"`)
    fetchOptions: { cache: 'no-store' }
    // you can override the default `fetchOptions` on a per query basis
    // via the `context` property on the options passed as a second argument
    // to an Apollo Client data fetching hook, e.g.:
    // const { data } = useSuspenseQuery(MY_QUERY, { context: { fetchOptions: { cache: "force-cache" }}});
  })

  return new NextSSRApolloClient({
    // use the `NextSSRInMemoryCache`, not the normal `InMemoryCache`
    cache: new NextSSRInMemoryCache(),
    link:
      typeof window === 'undefined'
        ? ApolloLink.from([
          // in a SSR environment, if you use multipart features like
          // @defer, you need to decide how to handle these.
          // This strips all interfaces with a `@defer` directive from your queries.
          new SSRMultipartLink({
            stripDefer: true
          }),
          httpLink
        ])
        : httpLink
  })
}

// you need to create a component to wrap your app in
export function ApolloWrapper ({ children }) {
  return (
    <ApolloNextAppProvider makeClient={makeClient}>
      {children}
    </ApolloNextAppProvider>
  )
}
phryneas commented 4 months ago

I mean... why do you want to do that in the first place? That's an internal property used by the Apollo Devtools, and we're in the process to change the registration process there.

If you want to look at your cache, the best place to go about it would be using the Apollo Devtools.

If you want to interact with it, the best way of going about that would be that in your own code you do something like window.myCacheVariable = cache so you "own" the variable that it's set to.

jsindos commented 4 months ago

@phryneas I was just going off the official documentation for SSR on the apollo docs which also places the cache on window.__APOLLO_STATE__ and thought it would also apply here.

It's also the way I've been verifying the cache during development outside of a Next.js environment, but I can start using Apollo Devtools.

phryneas commented 4 months ago

That one documents window.__APOLLO_STATE__ - not window.__APOLLO_CLIENT__.

The first one is a variable name used in non-streaming SSR - but honestly it's just a suggestion, you could make up any variable name there. Your code writes it, your code reads it, Apollo Client is not aware of it.

The latter one is an implementation detail of Apollo Client that is used in a legacy approach of connecting to the devtools. I just double-checked: we don't document that anywhere, and you cannot rely on it.

Either way: you don't have to follow that tutorial. It describes how you do SSR if you manually set up SSR in an application with renderToString - but the Next.js app directory sets up SSR for you in a very different and opinionated way - it doesn't use renderToString, but renderTo*Stream. The method there varies a lot, since the framework takes over SSR for you, and the application starts running in the browser before the server is even finished rendering - an approach like before, where you transport all the data over at once cannot work there anymore. That's why this package exists, to take over all that work for you :)

jsindos commented 4 months ago

@phryneas Awesome, thanks for clearing that up. The last time I used Next.js was version 2 and I don't recall seeing any of this streaming stuff then, so this helps clarify a lot.

phryneas commented 4 months ago

Great I could clear things up. I'm going to close the issue here now, but if you have any more follow-up questions, please don't hesitate to ask :)