question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Next JS 13 (AppDir and RSC) with ApolloClient

See original GitHub issue

Intended outcome: After moving my app from the pages layout to the new app directory, I should be able to make simple queries to my GraphQL endpoint using apollo-client and graphql-codegen

Actual outcome: With the new NextJS 13 server rendered components, its no longer possible to provide the apollo context at the root of the app and then useQuery() to fetch the data.

How to reproduce the issue: I initially tried to match up the pageProps that I was using in the previous version:

config/apolloClient.ts
export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__";

let apolloClient: ApolloClient<NormalizedCacheObject> | null = null;

type SchemaContext =
  | SchemaLink.ResolverContext
  | SchemaLink.ResolverContextFunction;

function createIsomorphicLink(_?: SchemaContext) {
  const httpLink = new HttpLink({
    uri: `${
      process.env.NODE_ENV == "production"
        ? process.env.GRAPHQL_URL_PROD
        : "http://localhost:5001/life-hive/us-central1/graphql"
    }`,
    credentials: "same-origin",
  });
  return from([httpLink]);
}

function createApolloClient(ctx?: SchemaContext) {
  return new ApolloClient({
    name: "life-hive-dashboard",
    ssrMode: typeof window === "undefined",
    link: createIsomorphicLink(ctx || undefined),
    cache: new InMemoryCache({
      typePolicies: {
        Customer: { keyFields: ["customer_id"] },
        ApiaryDevice: { keyFields: ["device_id"] },
        HiveDevice: { keyFields: ["device_id"] },
        CombDevice: { keyFields: ["comb_id"] },
        Treatment: { keyFields: ["treatment_id", "device_id"] },
        DeviceEvent: { keyFields: ["event_id"] },
        CombState: { keyFields: ["id"] },
        DeviceState: { keyFields: ["id"] },
        Failure: { keyFields: ["failure_id"] },
        Query: {
          fields: {
            getGlobalIDs: relayStylePagination(),
          },
        },
      },
    }),
  });
}

interface InitApollo {
  initialState?: any;
  ctx?: SchemaContext;
}

export function initializeApollo({ ctx, initialState }: InitApollo) {
  const _apolloClient = apolloClient ?? createApolloClient(ctx || undefined);

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

    // Merge the initialState from getStaticProps/getServerSideProps in the existing cache
    const data = merge(existingCache, initialState, {
      // combine arrays using object equality (like in sets)
      arrayMerge: (destinationArray, sourceArray) => [
        ...sourceArray,
        ...destinationArray.filter((d) =>
          sourceArray.every((s) => !isEqual(d, s)),
        ),
      ],
    });

    // Restore the cache with the merged data
    _apolloClient.cache.restore(data);
  }
  // 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;
}

export function addApolloState(
  client: ApolloClient<NormalizedCacheObject>,
  pageProps: { props: any },
) {
  pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();

  return pageProps;
}

export function useApollo(pageProps: any) {
  const state = pageProps[APOLLO_STATE_PROP_NAME];
  const store = useMemo(
    () => initializeApollo({ initialState: state }),
    [state],
  );
  return store;
}
pages/_app.tsx
function MyApp({ Component, pageProps }: AppProps) {
  const router = useRouter();
  const apolloClient = useApollo(pageProps);
  return (
    <ApolloProvider client={apolloClient}>
          <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default MyApp;

With the new Next13 AppDir they suggest creating a provider.tsx which is rendered client side and used to wrap the children in the layout.tsx as stated in their docs, but since i’m adding the apollo state to the app in my useApollo() this doesn’t work.

So i’ve tried to make a simplier version by following some other blog posts on using Next and Apollo to initialize the client and then try useQuery() in a RSC:

config/apollo_config.ts
function createApolloClient() {
  return new ApolloClient({
    name: 'internal-dashboard',
    uri: process.env.GRAPHQL_URL_PROD,
    cache: new InMemoryCache(),
  });
}

export function useApollo() {
  const client = useMemo(() => createApolloClient(), []);
  return client;
}
app/providers.tsx
'use client';

import { ApolloProvider } from '@apollo/client';
import { useApollo } from '../config/apollo_client';

export default function Provider({ children }: { children: React.ReactNode }) {
  const client = useApollo();
  return <ApolloProvider client={client}>{children}</ApolloProvider>;
}

When running on a server rendered component i get the following error:

TypeError: Cannot read properties of undefined (reading 'Symbol(__APOLLO_CONTEXT__)')
    at Object.getApolloContext (webpack-internal:///(sc_server)/./node_modules/@apollo/client/react/context/context.cjs:22:49)
    at useApolloClient (webpack-internal:///(sc_server)/./node_modules/@apollo/client/react/hooks/hooks.cjs:27:46)
    at Object.useQuery (webpack-internal:///(sc_server)/./node_modules/@apollo/client/react/hooks/hooks.cjs:100:29)
    at useProductsQuery (webpack-internal:///(sc_server)/./graphql/generated/graphql-codegen.tsx:239:56)
    at ProductContent (webpack-internal:///(sc_server)/./app/(product)/ProductContent.tsx:12:125)
    at attemptResolveElement (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1207:42)
    at resolveModelToJSON (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1660:53)
    at Object.toJSON (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1121:40)
    at stringify (<anonymous>)
    at processModelChunk (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:172:36)
    at retryTask (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1868:50)
    at performWork (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1906:33)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1297:40)
    at scheduleWork (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:52:25)
    at pingTask (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1296:29)
    at ping (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1309:40)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

and if i run it on a client rendered component i get:

fetch is not defined
    at new ApolloError (index.js?2dcd:29:1)
    at eval (QueryManager.js?f2a8:609:1)
    at both (asyncMap.js?acdc:16:46)
    at eval (asyncMap.js?acdc:9:57)
    at new Promise (<anonymous>)
    at Object.then (asyncMap.js?acdc:9:1)
    at Object.eval [as next] (asyncMap.js?acdc:17:1)
    at notifySubscription (module.js?4392:132:1)
    at onNotify (module.js?4392:176:1)
    at SubscriptionObserver.next (module.js?4392:225:1)
    at eval (iteration.js?8787:4:50)
    at Array.forEach (<anonymous>)
    at iterateObserversSafely (iteration.js?8787:4:1)
    at Object.next (Concast.js?c3b3:25:43)
    at notifySubscription (module.js?4392:132:1)
    at onNotify (module.js?4392:176:1)
    at SubscriptionObserver.next (module.js?4392:225:1)
    at eval (parseAndCheckHttpResponse.js?5a22:123:1)

At this point i’m just kind walking around in the dark as i can’t really wrap my head around what needs to be happening in order for the app to work with client rendered and server rendered components and where apollo client fits in to get my graphql data that i need.

Any suggestions or links to working examples for someone using Next JS 13 + AppDir + ApolloClient would be much appreciated!

Versions

  System:
    OS: Windows 10 10.0.22621
  Binaries:
    Node: 18.10.0 - D:\Program Files\nodejs\node.EXE
    Yarn: 1.22.19 - D:\Program Files\nodejs\yarn.CMD
    npm: 8.19.2 - D:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.22621.819.0), Chromium (108.0.1462.42)
  npmPackages:
    @apollo/client: ^3.7.2 => 3.7.2

Issue Analytics

  • State:open
  • Created 9 months ago
  • Reactions:1
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
lloydrichardscommented, Dec 20, 2022

I did get it working with Apollo client but it invoked doing some weird things with wrapping the layout.tsx in a provider.tsx that lead flagged with “use client” and then whenever Apollo queries were called also using the same flag. This really missed the point of using the app dir for me so I ended up using grapwhl-request instead of Apollo client and everything works using just the app dir and RSC

On Tue, 20 Dec 2022, 19:17 Michiel Sikma, @.***> wrote:

I’m curious if there’s any information yet about what the recommended interface will look like for using Apollo with Next 13.

For example, this blog post https://www.apollographql.com/blog/apollo-client/next-js/next-js-getting-started/ for Next 12 talks about using the old getStaticProps() and getServerSideProps() api that is not supported in the /app dir.

I realize this is all very new and I probably should not be using the /app dir yet if I want to use Apollo, but some thoughts on this would be appreciated since as far as I can tell there’s no real documentation on this yet.

— Reply to this email directly, view it on GitHub https://github.com/apollographql/apollo-client/issues/10344#issuecomment-1359949666, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOHWIQKON2NDZXGBHC2QTJTWOHZ45ANCNFSM6AAAAAASZL4AY4 . You are receiving this because you were mentioned.Message ID: @.***>

0reactions
msikmacommented, Dec 20, 2022

I’m curious if there’s any information yet about what the recommended interface will look like for using Apollo with Next 13.

For example, this blog post for Next 12 talks about using the old getStaticProps() and getServerSideProps() api that is not supported in the /app dir.

I realize this is all very new and I probably should not be using the /app dir yet if I want to use Apollo, but some thoughts on this would be appreciated since as far as I can tell there’s no real documentation on this yet.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How could I go about ISR in NextJS 13 with Apollo client? - Help
With the new NextJS 13, to create ISR, we are using the native fetch Web API instead of getStaticProps and I couldn't find...
Read more >
Blog - Next.js 13
Next.js 13 introduces layouts, React Server Components, and streaming in the app directory, as well as Turbopack, an improved image ...
Read more >
Next.js 13: Layouts, React Server Components (async ... - Reddit
I think Apollo Client has the worst implementation of SSR. I was using for a long time and eventually moved to React Query...
Read more >
Working with GraphQL and Next.js 13 React Server ... - Grafbase
This guide assumes you have a Next.js 13 app setup and using the new ... experimental: { appDir: true } } module.exports =...
Read more >
Joao Garin (@joaogarin) / Twitter
One of the most requested features of Next.js for Drupal has been GraphQL ... Since data fetching will happen in RSC (server) and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found