apollo-client-nextjs: registerApolloClient is not a function (Runtime error)

Hey there. This is a first time I’m using Next JS 14 and Apollo client. I’ve done ApolloProvider and apollo-client.ts like in the documentation. In MainIntro I use an SSR request to preload the data that I use in the child component. The Header component must be client-side because I’m going to use hooks. As soon as I add “use client” to Header.tsx, I get the described error. Please help, below I post the component code and errors.

error Screenshot 2024-04-03 at 13 26 34

apollo-client.ts

import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc";

export const { getClient } = registerApolloClient(() => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: new HttpLink({
      // this needs to be an absolute url, as relative urls cannot be used in SSR
      uri: process.env.API_URL,
      // 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" },
    }),
  });
});

apollo-wrapper.tsx

"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:3000/api/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 }: React.PropsWithChildren) {
  return (
    <ApolloNextAppProvider makeClient={makeClient}>
      {children}
    </ApolloNextAppProvider>
  );
}

lauout.tsx

export default function RootLayout({
  children,
  params: { locale },
}: Readonly<Props>) {
  return (
    <html lang={locale}>
      <body className={inter.className}>
        <ApolloWrapper>
          <Header />
          <main>{children}</main>
          <Footer />
        </ApolloWrapper>
      </body>
    </html>
  );
}

page.tsx

import { MainIntro } from "@/components";

const HomePage = () => {

  return (
    <>
      <MainIntro />
    </>
  );
};

export default HomePage;

MainIntro.tsx

import { gql } from "@apollo/client";
import { getTranslations, getMessages } from "next-intl/server";
import { NextIntlClientProvider } from "next-intl";
import pick from "lodash/pick";
import { getClient } from "@/configs";
import { Container } from "@/components";
import { FilterClient } from "./components";
import styles from "./MainIntro.module.css";

const MainIntro = async () => {
  const t = await getTranslations("Homepage");
  const messages = await getMessages();
  const { data } = await getClient().query({
    query: FILTER_QUERY,
    context: {
      fetchOptions: {
        next: { revalidate: 3600 },
      },
    },
  });

  return (
    <section className={styles.section}>
      <Container className={styles.container}>
        <h1 className={styles.title}>{t("title")}</h1>
        <p className={styles.description}>
          Discover your perfect match in healthcare with our service, where
          finding a trusted specialist is both convenient and swift, ensuring
          peace of mind with every choice
        </p>
        <NextIntlClientProvider messages={pick(messages, "Global")}>
          <FilterClient data={data} />
        </NextIntlClientProvider>
      </Container>
    </section>
  );
};

const FILTER_QUERY = gql`
  query Query {
    countries {
      id
      code
    }
    specialties {
      id
      key
    }
  }
`;

export default MainIntro;

header.tsx

"use client"
import React from "react";
import Link from "next/link";
import Image from "next/image";
import styles from "./header.module.css";
import { Container } from "@/components";
import { LangSwitcher } from "./components";

type Props = {
  simple?: boolean;
};

const Header:React.FC<Props> = ({ simple }) => {
  return (
    <header className={styles.section}>
      <Container className={styles.wrapper}>
        <Link href="/" className={styles.logo} aria-label="Veso Health Logo">
          <Image
            src="/veso-logo.svg"
            width={103}
            height={34}
            alt="Veso Health Logo"
            priority={true}
          />
        </Link>
        {!simple && <LangSwitcher />}
      </Container>
    </header>
  );
};

export default Header;

About this issue

  • Original URL
  • State: closed
  • Created 3 months ago
  • Comments: 18 (8 by maintainers)

Most upvoted comments

A Client Component is never allowed to import from a file that contains a React Server Component, because that “makes it a Client Component”. In some cases it’s just a bundling error, in some cases it can even pose a security risk.

Thanks for your help