Passing as Props vs Extracting Cache Apollo Client Nextjs
Asked Answered
B

1

6

I posted this in the apollo-client repo, but thought I would ask stackoverflow as well

Hello! I'm new to apollo (and graphql as a whole) and am having some questions about SSR / SSG. I conceptually know what SSR/SSG is and how it works, but not so much with apollo-client.

I've tried searching and scouring the web for which way to do this correctly and have seen both versions with little explanation of why, so my goal with this post to have a place to point and go "This is why you do one over the other".

What are the upsides / downsides between doing

This is a mix of TypeScript and psuedo-code please don't critique syntax kthx

// apolloClient.ts
const client = new ApolloClient({
  link: new HttpLink({ uri: '/graphql' }),
  cache: new InMemoryCache(),
});
// component.tsx
import client from '../apolloClient';

const Component: FunctionalComponent = ({ data }) => {
   ...
}

export const getServerSideProps: GetServerSideProps = async () => {
  const { data } = client.query(...);

  return {
    props: { data }
  }
}
// app.tsx
import client from './client';

const MyApp: NextComponentType<AppContext, AppInitialProps, AppProps> = ({ Component, pageProps }) => (
  <ApolloProvider client={client}>
    <Component {...pageProps} />
  </ApolloProvider>
);

VS

// apolloClient.ts
const createClient = () => new ApolloClient({
  link: new HttpLink({ uri: '/graphql' }),
  cache: new InMemoryCache(),
  ssrMode: typeof window === 'undefined',
});

let client: ApolloClient<NormalizedCacheObject>;
const initalizeApollo = (initalState?: NormalizedCacheObject) => {
  const apolloClient = client ?? createClient();

  if (initalState) {
    apolloClient.cache.restore({
      ...apolloClient.cache.extract(),
      ...initalState,
    });
  }

  if (typeof window === 'undefined') return apolloClient;

  client ??= apolloClient;

  return client;
}

const useApollo = (initalState?: NormalizedCacheObject) => useMemo(() => initalizeApollo(initalState), [initalState]);
// component.tsx
import { useQuery } from 'apollo-client';
import useApollo from '../apolloClient';

const Component = () => {
   const { data } = useQuery(...);
}

export const getServerSideProps: GetServerSideProps = async () => {
  const client = useApollo();

  await client.query(...);

  return {
    props: { initalApolloState: client.cache.extract() }
  }
}
// app.tsx
const MyApp: NextComponentType<AppContext, AppInitialProps, AppProps> = ({ Component, pageProps }) => {
  const client = useApollo(pageProps.initalApolloState);

  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  )
};

This is my own "I don't understand" portion of this discussion

To me it seems doing it the second way (with SSR?) you're having to run queries twice and write a lot of extra code to get the same effect? What are the performance / safety / any benefits at all to either method.

Thanks!

Bookstore answered 15/4, 2021 at 21:32 Comment(0)
N
1

Your first approach is more in line with what Next is trying to do. It wants to pull data at build time for pages via getStaticProps (SSG) or getServerSideProps (SSR). The idea is that it will generate pages on the server statically whenever it can.

Apollo Client doesn't need to be referenced in the getStaticProps/getServerSideProps call. I think you could get away with using Context and not even need Apollo Client the way your code is written.

Where I use Apollo in Next apps is when I need to pull data into components. Next doesn't really have an answer for that but it's what Apollo does really well. A good use case is a menu that appears on every page. You don't want to trigger a site-wide static regeneration if you change on menu item. Apollo will keep all the pages fresh and won't trigger Next to render again.

Outline of what I have been doing in this answer.

Ninnyhammer answered 15/4, 2021 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.