-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
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