apollo-client-nextjs icon indicating copy to clipboard operation
apollo-client-nextjs copied to clipboard

Error: (0 , _apollo_client_integration_nextjs__WEBPACK_IMPORTED_MODULE_2__.registerApolloClient) is not a function

Open DanielGBullido opened this issue 10 months ago • 5 comments

Image

Of course! Here's the English translation of your corrected and polished message:

We are trying to integrate the following versions:

"@apollo/client": "3.13.5",
"@apollo/client-integration-nextjs": "0.12.0"

However, we are finding the documentation very difficult to understand, as it is quite confusing and unclear.

Our issue arises when we try to use useApolloClient() from within a provider wrapped like this:

const ApolloWrapper = ({ children }: ApolloProviderProps) => {
  return <ApolloNextAppProvider makeClient={getClient}>{children}</ApolloNextAppProvider>;
}

We’re getting the error shown in the attached image. We’ve followed your Next.js examples repeatedly (we’re using version 18), but we still can’t get it to work properly.

We have several questions we'd like to clarify:

  1. What is the correct way to make a client-side request using Apollo?
  2. Do the apolloClient and ApolloWrapper files need to be placed in a specific directory?
  3. Should the makeClient value in ApolloWrapper be the same as the one defined using registerApolloClient, or should we import getClient from that file and pass it directly as makeClient?
  4. Server-side requests are working fine — the issue is only on the client side.

We would greatly appreciate a clear explanation or an up-to-date working example. Thank you very much for your support.

DanielGBullido avatar Apr 10 '25 16:04 DanielGBullido

Did you maybe mix server code and client code in the same file?

ApolloWrapper needs to be in a file with "use client" at the top. registerApolloClient needs to be in a server file, though. That means a file without "use client", and that file must never be imported by a file with "use client" at the top, and never be imported by a file that is imported by a file with "use client" at the top (etc.). As "use client" is "spreading" that way, it would turn the server file into a client file.

It seems that here you create some kind of import chain from a "use client" file to your registerApolloClient call.

phryneas avatar Apr 11 '25 07:04 phryneas

👋 Hi there!

We’ve split our Apollo Client setup like this:

src/lib/apollo/apolloClient.ts

import { signOut } from 'next-auth/react'
import { ROUTES } from '@/utils/constants/routes'
import {
  ApolloError,
  ApolloLink,
  FetchResult,
  HttpLink,
  NextLink,
  Operation,
} from '@apollo/client'
import {
  ApolloClient,
  InMemoryCache,
  registerApolloClient,
} from '@apollo/client-integration-nextjs'
import 'cross-fetch/polyfill'
import { GRAPHQL_AUTHORIZATION_ERROR } from './constants/graphql-errors'

const httpLink = new HttpLink({
  uri: `${process.env.NEXT_PUBLIC_BACKEND_URL}/graphql`,
  headers: {},
})

const verifyGraphQLErrors = new ApolloLink((operation: Operation, forward: NextLink) => {
  return forward(operation).map((response: FetchResult) => {
    if (response?.errors && response?.errors[0]) {
      switch (response.errors[0]?.extensions?.category) {
        case GRAPHQL_AUTHORIZATION_ERROR:
          if (typeof localStorage !== 'undefined') {
            signOut({
              callbackUrl: `/${window.location.pathname.split('/')[1]}${ROUTES.SIGN_OUT}?error=invalidToken`,
            }).catch(console.error)
          }
          break
        default:
          throw new ApolloError({
            graphQLErrors: response.errors,
            errorMessage: response.errors?.[0]?.message || 'Error',
          })
      }
    }
    return response
  })
})

export const { getClient, query, PreloadQuery } = registerApolloClient(() => {
  return new ApolloClient({
    link: verifyGraphQLErrors.concat(httpLink),
    cache: new InMemoryCache({ addTypename: false }),
    defaultOptions: {
      mutate: { fetchPolicy: 'no-cache', errorPolicy: 'all' },
      query: { fetchPolicy: 'no-cache', errorPolicy: 'all' },
    },
  })
})

src/lib/apollo/ApolloWrapper.tsx

'use client'

import { ReactNode } from 'react'
import {
  ApolloClient,
  ApolloNextAppProvider,
  InMemoryCache,
} from '@apollo/client-integration-nextjs'
import {
  ApolloError,
  ApolloLink,
  FetchResult,
  HttpLink,
  NextLink,
  Operation,
} from '@apollo/client'
import { GRAPHQL_AUTHORIZATION_ERROR } from '@/lib/apollo/constants/graphql-errors'
import { signOut } from 'next-auth/react'
import { ROUTES } from '@/utils/constants/routes'

interface ApolloProviderProps {
  children: ReactNode
}

function makeClient() {
  const httpLink = new HttpLink({
    uri: `${process.env.NEXT_PUBLIC_BACKEND_URL}/graphql`,
    headers: {},
  })

  const verifyGraphQLErrors = new ApolloLink((operation: Operation, forward: NextLink) => {
    return forward(operation).map((response: FetchResult) => {
      if (response?.errors && response?.errors[0]) {
        switch (response.errors[0]?.extensions?.category) {
          case GRAPHQL_AUTHORIZATION_ERROR:
            if (typeof localStorage !== 'undefined') {
              signOut({
                callbackUrl: `/${window.location.pathname.split('/')[1]}${ROUTES.SIGN_OUT}?error=invalidToken`,
              }).catch(console.error)
            }
            break
          default:
            throw new ApolloError({
              graphQLErrors: response.errors,
              errorMessage: response.errors?.[0]?.message || 'Error',
            })
        }
      }
      return response
    })
  })

  return new ApolloClient({
    link: verifyGraphQLErrors.concat(httpLink),
    cache: new InMemoryCache({ addTypename: false }),
    defaultOptions: {
      mutate: { fetchPolicy: 'no-cache', errorPolicy: 'all' },
      query: { fetchPolicy: 'no-cache', errorPolicy: 'all' },
    },
  })
}

const ApolloWrapper = ({ children }: ApolloProviderProps) => {
  return <ApolloNextAppProvider makeClient={makeClient}>{children}</ApolloNextAppProvider>
}

export default ApolloWrapper

These are completely separated.

But when we use this in a page:

<ApolloWrapper>
  <Suspense fallback={<>Loading</>}>
    <WebSettingsContextProvider>{children}</WebSettingsContextProvider>
  </Suspense>
</ApolloWrapper>

And inside WebSettingsContextProvider we use:

'use client'

import { useApolloClient } from '@apollo/client'
import { Session } from 'next-auth'
import { Store } from '@/types/ContextOptions'
import { customContext } from '@/utils/customContext'
import { OperationNames } from '../operationNames'
import {
  GetWebSettingsDocument,
  GetWebSettingsQuery,
} from '@/lib/graphql-operations'
import { ApolloQueryResult } from '@apollo/client'

export const useQueryGetWebSettings = () => {
  const apolloClient = useApolloClient()

  return async (session: Session | null, store: Store): Promise<ApolloQueryResult<GetWebSettingsQuery>> => {
    const context = customContext({
      operationName: OperationNames._QUERY_GET_WEB_SETTINGS,
      session,
      store,
    })

    return await apolloClient.query({
      query: GetWebSettingsDocument,
      context,
    })
  }
}

We assumed that since this component is under <ApolloWrapper>, useApolloClient() would use the correct client. However, we keep getting this error:

Unhandled Runtime Error

Error: (0 , _apollo_client_integration_nextjs__WEBPACK_IMPORTED_MODULE_2__.registerApolloClient) is not a function

src/lib/apollo/apolloClient.ts (33:71)

Whether we wrap it in Suspense or not, it doesn’t work.

We’ve checked the official examples from the repo, but none of them have helped so far: 👉 https://github.com/apollographql/apollo-client-integrations/tree/%40apollo/client-integration-nextjs%400.12.0/examples


Versions:

"@apollo/client": "3.13.5",
"@apollo/client-integration-nextjs": "0.12.0",
"next": "15.2.5",
"next-auth": "4.24.11",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-cookie": "8.0.1",
"react-gtm-module": "2.0.11",
"react-intl": "7.1.10",
"react-jsbarcode": "1.0.1",
"react-modal": "3.16.3",
"react-range": "1.10.0",
"react-slick": "0.30.3",
"react-to-print": "3.0.5",
"react-waypoint": "10.3.0",
"react-zendesk": "0.1.13"

Any help would be appreciated 🙏

DanielGBullido avatar Apr 11 '25 08:04 DanielGBullido

@DanielGBullido

Versions:

"next": "15.2.5", "next-auth": "4.24.11", "react": "18.3.1", "react-dom": "18.3.1",

to my knowledge you can't use React 18 with Next 15? https://nextjs.org/docs/app/guides/upgrading/version-15#react-19

ldeveber avatar May 23 '25 14:05 ldeveber

We are encountering the same error.

It seems the logic is trying to import from @apollo/client-integration-nextjs/dist/index.browser.js instead of index.rsc.js from what I gathered.

Also verified with AI that our separation between server and client files is correct.

mcmxcdev avatar May 29 '25 19:05 mcmxcdev

It seems the logic is trying to import from @apollo/client-integration-nextjs/dist/index.browser.js instead of index.rsc.js from what I gathered.

Yes, that's what happens if you are in a client file. It imports the index.browser.js in client files and the index.rsc.js in RSC.

If you look at https://github.com/apollographql/apollo-client-integrations/blob/c465affd9e31674e868cdc7ac90cf0cab4c7f70e/packages/nextjs/package.json#L45-L47 you will see that react-server has a higher priority than browser, so if you are in an RSC environment, you bundler will pick up the RSC import.

Also verified with AI that our separation between server and client files is correct.

I fear AI is probably missing something there :/

You can try to add import 'server-only' at the top of the files you want to be server only. If you get an error message regarding that, they are in fact imported from a client import path.

phryneas avatar May 30 '25 10:05 phryneas