Skip to content

Apollo Client Setup ​

Apollo Client is the primary data management layer for the Bagisto Headless storefront. This guide walks through its configuration, integration with Next.js, and best practices for e-commerce performance.

1. Key Features ​

Apollo Client provides several critical capabilities for this project:

  • Intelligent Caching: Automatic normalization and updates for consistent UI.
  • SSR Compatibility: Optimized for Next.js Server Components and SEO.
  • Auth Integration: Seamless handling of customer API tokens.
  • Type Safety: Full TypeScript support for GraphQL queries and mutations.

2. Installation ​

Install the core dependencies using Yarn:

bash
yarn add @apollo/client graphql next-auth

NOTE

Ensure you are using @apollo/client v3.11.0 or higher for best compatibility with React 19 and Next.js 16.

3. Core Implementation ​

The Apollo Client is configured in src/lib/apollo-client.ts with authentication and SSR support.

The following files constitute the heart of the Apollo setup in this project.

ts
// src/lib/apollo-client.ts
import { GRAPHQL_URL } from "@/utils/constants";
import { ApolloClient, HttpLink, InMemoryCache, from } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { getSession } from "next-auth/react";

export default function initializeApollo() {
  const ssrMode = typeof window === "undefined";
  const cache = new InMemoryCache();

  const httpLink = new HttpLink({
    uri: GRAPHQL_URL,
    credentials: "include",
  });

  const authLink = setContext(async (_, { headers }) => {
    if (ssrMode) {
      return { headers };
    }

    const session = await getSession();
    const userToken = session?.user?.accessToken;

    return {
      headers: {
        ...headers,
        ...(userToken && { authorization: `Bearer ${userToken}` }),
        "Content-Type": "application/json",
      },
    };
  });

  return new ApolloClient({
    ssrMode,
    link: from([authLink, httpLink]),
    cache: cache,
    defaultOptions: {
      watchQuery: {
        fetchPolicy: ssrMode ? "network-only" : "cache-first",
        nextFetchPolicy: ssrMode ? "network-only" : "cache-first",
      },
      query: {
        fetchPolicy: ssrMode ? "network-only" : "cache-first",
      },
    },
  });
}

📖 Key Features:

  • SSR Detection: Automatically detects server vs. client environment
  • Dynamic Authentication: Injects Bearer token from NextAuth session
  • Smart Caching: Network-only on server, cache-first on client
  • Bagisto GraphQL Endpoint: Uses environment-based URL configuration

4. Fetch Policy Strategy ​

We use different fetch policies based on the context to balance data freshness and application speed.

PolicyEnvironmentUse Case
network-onlyServer (SSR)Ensuring SEO bots and users see the latest product data.
cache-firstClient (CSR)Providing instant transitions for previously visited categories.
no-cacheBothHandling highly sensitive or unique transactional data.

5. Usage Examples ​

Hover or swipe to see how to use Apollo in different parts of the application.

5.1 Client Component – Query Example ​

Using Apollo Client’s useQuery hook inside a Client Component.

tsx
"use client";

import { useQuery } from "@apollo/client";
import { GET_CATEGORIES } from "@/graphql/queries";

export function CategoryNav() {
  const { data, loading } = useQuery(GET_CATEGORIES);

  if (loading) return <Skeleton />;

  return (
    <ul>
      {data.categories.map((cat) => (
        <li key={cat.id}>{cat.name}</li>
      ))}
    </ul>
  );
}

5.2 Server Component – GraphQL Request ​

Fetching data on the server using a lightweight graphqlRequest utility.

tsx
import { graphqlRequest } from "@/lib/graphql-fetch";
import { GET_PRODUCT } from "@/graphql/queries";

export default async function ProductPage({ params }) {
  const { slug } = params;

  const data = await graphqlRequest(GET_PRODUCT, { slug });

  return <ProductView product={data.product} />;
}

5.3 Client Component – Mutation Example ​

Performing mutations (e.g. login) using Apollo's useMutation. javascript

tsx
"use client";

import { useMutation } from "@apollo/client";
import { LOGIN_MUTATION } from "@/graphql/mutations";

export function LoginForm() {
  const [login, { loading }] = useMutation(LOGIN_MUTATION);

  const onSubmit = (formData) => {
    login({ variables: { ...formData } });
  };

  return <Button onClick={onSubmit} loading={loading} />;
}

6. Performance & Error Handling ​

Query Batching ​

To optimize network overhead, we recommend enabling batching for high-traffic components.

typescript
import { BatchHttpLink } from "@apollo/client/link/batch-http";
// Replace HttpLink with BatchHttpLink in apollo-client.ts

Catch and log errors globally to improve observability.

ts
import { onError } from "@apollo/client/link/error";

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) console.error("[GQL Error]", graphQLErrors);
  if (networkError) console.error("[Network Error]", networkError);
});

7. Common Troubleshooting ​

> **SSR Hydration Mismatch:** Ensure that your `ApolloWrapper` is memoized (as shown in Step 3) to prevent the client from being recreated on every render, which can cause inconsistent HTML between server and client.

  • Missing Cookies: Ensure credentials: "include" is set in the HttpLink if your Bagisto backend relies on session cookies.
  • Cache Persistence: For larger catalogs, consider configuring persistent cache using apollo3-cache-persist.

📖 Continue Reading:

Released under the MIT License.