Skip to main content

Apollo Client and Environment Configuration

This guide explains how to properly set up Apollo Client in a React application using Vite, including environment variable configuration and best practices.

Table of Contents

  1. Basic Apollo Client Setup
  2. Environment Variables Configuration
  3. Advanced Apollo Client Configuration
  4. Integration with React

Basic Apollo Client Setup

The Apollo Client setup in your application is functional but could benefit from some enhancements. Here's a breakdown of your current implementation:

// src/config/apollo-client.ts
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";

const httpLink = new HttpLink({
uri: import.meta.env.VITE_BASE_URL,
});

const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});

export default apolloClient;

Environment Variables Configuration

Your environment variable setup is correct:

// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_BASE_URL: string;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}
// .env.local
VITE_BASE_URL="https://v3-symfony-app.oxagry.com/graphql";

Advanced Apollo Client Configuration

Here's an improved version of your Apollo Client setup with additional features:

// src/config/apollo-client.ts
import {
ApolloClient,
InMemoryCache,
HttpLink,
ApolloLink,
from,
} from "@apollo/client";
import { onError } from "@apollo/client/link/error";

// Error handling link
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.error(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
);
});
}

if (networkError) {
console.error(`[Network error]: ${networkError}`);
}
});

// HTTP connection to the API
const httpLink = new HttpLink({
uri: import.meta.env.VITE_BASE_URL,
// Include credentials if your API requires authentication
credentials: "include",
});

// Authentication middleware
const authMiddleware = new ApolloLink((operation, forward) => {
// Get the authentication token from local storage if it exists
const token = localStorage.getItem("token");

// Add the authorization to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
}));

return forward(operation);
});

// Cache configuration with type policies
const cache = new InMemoryCache({
typePolicies: {
// Example type policy for a Query field
Query: {
fields: {
// Example for a paginated field
items: {
// Concatenate results when fetching more items
keyArgs: false,
merge(existing = [], incoming) {
return [...existing, ...incoming];
},
},
},
},
},
});

// Initialize Apollo Client
const apolloClient = new ApolloClient({
link: from([errorLink, authMiddleware, httpLink]),
cache,
defaultOptions: {
watchQuery: {
fetchPolicy: "cache-and-network",
errorPolicy: "ignore",
},
query: {
fetchPolicy: "network-only",
errorPolicy: "all",
},
mutate: {
errorPolicy: "all",
},
},
});

export default apolloClient;

Integration with React

Your integration with React is correctly implemented:

// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import { ApolloProvider } from "@apollo/client";
import apolloClient from "@/config/apollo-client.ts";

createRoot(document.getElementById("root")!).render(
<StrictMode>
<ApolloProvider client={apolloClient}>
<App />
</ApolloProvider>
</StrictMode>
);