Next.js & Apollo: Troubleshooting Cookies Not Being Passed in Your HOC Configuration
Next.js and Apollo Client are a powerful duo for building modern, data-driven web applications. Next.js excels at server-side rendering (SSR), static site generation (SSG), and client-side navigation, while Apollo Client simplifies managing GraphQL data fetching, caching, and state. However, one common roadblock developers face is cookies not being passed correctly in Higher Order Component (HOC) configurations. This issue can break critical flows like authentication, session management, or personalized content—leaving users frustrated with "unauthorized" errors or missing data.
Cookies are often used to store JWT tokens, session IDs, or user preferences, making their reliable transmission between client and server essential. HOCs, which wrap components to inject data or functionality (e.g., withApollo for GraphQL data fetching), can inadvertently disrupt cookie flow if misconfigured, especially when mixing client-side and server-side rendering (SSR) in Next.js.
In this guide, we’ll demystify why cookies fail to pass in HOC setups, walk through step-by-step troubleshooting, and provide actionable solutions to ensure your cookies are transmitted reliably.
Table of Contents#
- Understanding the Problem: Why Cookies Matter in Next.js & Apollo
- Common Causes of Cookies Not Being Passed in HOCs
- Troubleshooting Steps: Diagnosing the Issue
- Step-by-Step Solution: Fixing Cookie Passing in HOCs
- Advanced Scenarios: Edge Cases and Workarounds
- Conclusion
- References
1. Understanding the Problem: Why Cookies Matter in Next.js & Apollo#
Before diving into solutions, let’s clarify why cookies are critical and how their transmission can fail in HOCs.
What Are Cookies Used For?#
Cookies are small pieces of data stored in the user’s browser, sent automatically with every HTTP request to the server. In Next.js + Apollo apps, they’re often used for:
- Authentication: Storing JWT tokens, session IDs, or OAuth tokens.
- Personalization: Tracking user preferences (e.g., theme settings).
- Server-Side Context: Passing metadata (e.g., A/B test groups) to the server.
The HOC and Cookie Flow#
HOCs like withApollo or custom data-fetching HOCs wrap components to inject GraphQL data. For cookies to work, they must be included in:
- Client-side requests: Apollo Client sends cookies from the browser to the GraphQL server.
- Server-side requests: Next.js (during SSR/SSG) sends cookies from the incoming HTTP request to the GraphQL server.
When cookies aren’t passed, the GraphQL server can’t authenticate the user, leading to 401 Unauthorized errors, missing user-specific data, or broken sessions.
2. Common Causes of Cookies Not Being Passed in HOCs#
To fix the issue, first identify its root cause. Here are the most frequent culprits:
2.1 Apollo Client Configuration Issues#
Apollo Client doesn’t send cookies by default. If you forget to configure credentials, cookies will never reach the server. Key misconfigurations include:
- Missing
credentialssetting inHttpLink(e.g., not settingcredentials: 'include'or'same-origin'). - Using Apollo Boost (deprecated) without explicitly enabling credentials.
- Overriding headers in context links (e.g.,
setContext) and accidentally stripping cookies.
2.2 Next.js Server-Side vs. Client-Side Rendering Differences#
Next.js has distinct data-fetching patterns (SSR, SSG, CSR), and cookies behave differently in each:
- SSG (
getStaticProps): Runs at build time, with no access to the incoming request. Cookies (user-specific) can’t be used here. - SSR (
getServerSideProps): Runs per request, with access toreq.headers.cookie. If HOCs usegetStaticPropsinstead ofgetServerSideProps, cookies are unavailable. - Client-side rendering (CSR): Relies on the browser’s cookie store, but Apollo Client may not send them without
credentials: 'include'.
2.3 HOC Implementation Pitfalls#
HOCs can inadvertently block cookies if:
- They hardcode data-fetching logic to use
getStaticProps(unsuitable for dynamic, user-specific data). - They fail to pass server-side cookies from
getServerSidePropsto Apollo Client. - They use outdated patterns (e.g.,
withApolloHOCs not compatible with Next.js 13+ App Router).
2.4 CORS and Server Configuration#
Even if the client sends cookies, the server may reject them due to misconfigured CORS:
- Missing
Access-Control-Allow-Credentials: trueheader on the GraphQL server. - Using
Access-Control-Allow-Origin: '*'with credentials (browsers block this—origin must be explicit). - Mismatched domains (e.g., client on
http://localhost:3000and server onhttps://api.example.comwithout CORS allowlisting).
3. Troubleshooting Steps: Diagnosing the Issue#
Use these steps to pinpoint where cookies are getting stuck:
3.1 Check Network Requests (Client-Side)#
Use your browser’s DevTools (Network tab) to inspect Apollo Client requests:
- Filter for GraphQL requests (look for
POSTto your GraphQL endpoint). - Check the Request Headers for a
Cookiefield. If missing, Apollo Client isn’t sending cookies. - Check the Response Headers for
Set-Cookie(if the server is supposed to set cookies) andAccess-Control-Allow-Credentials: true.
3.2 Inspect Server-Side Requests (Next.js SSR)#
For server-side requests (e.g., during SSR), log the incoming cookies in getServerSideProps:
// pages/protected-page.js
export async function getServerSideProps(ctx) {
console.log("Server-side cookies:", ctx.req.headers.cookie); // Check if cookies appear here
return { props: {} };
}If ctx.req.headers.cookie is empty, cookies aren’t reaching Next.js’s server. If it’s populated but Apollo Client still fails, the HOC isn’t passing them to Apollo.
3.3 Verify Apollo Client Setup#
Audit your Apollo Client configuration. Ensure:
HttpLinkincludescredentials: 'include'(for cross-origin requests) or'same-origin'(for same-domain requests).- No conflicting headers are stripping cookies (e.g., in
setContext):// Bad: Overriding headers without preserving existing ones const authLink = setContext((_, { headers }) => ({ headers: { authorization: `Bearer ${token}`, // Accidentally removes cookies! }, })); // Good: Merge with existing headers const authLink = setContext((_, { headers }) => ({ headers: { ...headers, // Preserves cookies and other headers authorization: `Bearer ${token}`, }, }));
3.4 Review HOC Data Fetching Logic#
Check if your HOC uses the right data-fetching method:
- If the HOC wraps a page using
getStaticProps, switch togetServerSideProps(SSG can’t access request cookies). - Ensure the HOC passes server-side cookies from
getServerSidePropsto Apollo Client (see Section 4 for examples).
4. Step-by-Step Solution: Fixing Cookie Passing in HOCs#
Follow these steps to ensure cookies are passed reliably in your HOC.
4.1 Configure Apollo Client with Credentials#
First, ensure Apollo Client sends cookies in all requests. Use createHttpLink with credentials enabled:
// lib/apollo-client.js
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
// 1. Create HTTP link with credentials
const httpLink = createHttpLink({
uri: 'https://your-graphql-server.com/graphql', // Your GraphQL endpoint
credentials: 'include', // Critical: Sends cookies in client-side and server-side requests
});
// 2. Optional: Add auth headers (merge with existing headers!)
const authLink = setContext((_, { headers }) => ({
headers: {
...headers, // Preserve existing headers (including cookies)
// Add custom headers here (e.g., API keys)
},
}));
// 3. Initialize Apollo Client
export function createApolloClient() {
return new ApolloClient({
link: authLink.concat(httpLink), // Combine links
cache: new InMemoryCache(),
});
}4.2 Handle SSR/SSG in Next.js with Cookies#
For server-side rendering, pass cookies from getServerSideProps to Apollo Client. Use a withApollo HOC to inject cookies into server-side requests:
// lib/withApollo.js
import { ApolloProvider } from '@apollo/client';
import { createApolloClient } from './apollo-client';
export function withApollo(PageComponent) {
const WithApollo = ({ apolloClient, pageProps }) => (
<ApolloProvider client={apolloClient}>
<PageComponent {...pageProps} />
</ApolloProvider>
);
// 1. Server-side: Fetch data with cookies from the request
WithApollo.getServerSideProps = async (ctx) => {
const { req } = ctx;
const apolloClient = createApolloClient();
// 2. Pass server-side cookies to Apollo Client
if (req) {
// Inject cookies into Apollo's HTTP link headers
apolloClient.link.options.headers.cookie = req.headers.cookie || '';
}
// 3. Fetch data (if the page has getServerSideProps)
const pageProps = await PageComponent.getServerSideProps?.(ctx) || {};
return {
...pageProps,
props: {
...pageProps.props,
apolloClient: apolloClient, // Pass client to the component
},
};
};
return WithApollo;
}4.3 Correct HOC Implementation for Data Fetching#
Use the withApollo HOC in your page components, and ensure you use getServerSideProps for dynamic, cookie-dependent data:
// pages/protected-page.js
import { useQuery } from '@apollo/client';
import { withApollo } from '../lib/withApollo';
import { GET_USER_DATA } from '../graphql/queries';
function ProtectedPage() {
// Client-side: Apollo sends cookies automatically (thanks to `credentials: 'include'`)
const { data, error } = useQuery(GET_USER_DATA);
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>Loading...</div>;
return <h1>Welcome, {data.user.name}!</h1>;
}
// Wrap the page with Apollo HOC to handle SSR/CSR cookies
export default withApollo(ProtectedPage);
// Use getServerSideProps to ensure cookies are available server-side
export async function getServerSideProps(ctx) {
return { props: {} }; // Apollo HOC will handle data fetching
}4.4 Ensure CORS and Server Compatibility#
Your GraphQL server must allow credentials. For example, with an Express server:
// Express server (GraphQL API)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://your-nextjs-app.com', // Explicit origin (not '*'!)
credentials: true, // Critical: Allows cookies
}));
// Start server
app.listen(4000, () => console.log("GraphQL server running on port 4000"));5. Advanced Scenarios: Edge Cases and Workarounds#
5.1 Using getServerSideProps with HOCs in Next.js 13+ App Router#
Next.js 13+ App Router uses React Server Components (RSC) by default. For HOCs in the App Router:
- Use
asynccomponents withfetchor Apollo Client’sfetchQuery(no HOCs needed for data fetching). - Access cookies via
cookies()fromnext/headers:// app/protected-page/page.js import { getServerSession } from "next-auth/next"; import { cookies } from "next/headers"; export default async function ProtectedPage() { const sessionCookie = cookies().get("session")?.value; // Access cookies // Fetch data with Apollo Client using sessionCookie return <div>Protected Content</div>; }
5.2 Next.js Middleware and Cookie Handling#
Next.js Middleware can modify cookies (e.g., adding/removing them). If cookies disappear, check for conflicting middleware:
// middleware.js
export function middleware(req) {
const response = NextResponse.next();
// Avoid removing cookies accidentally:
response.cookies.set("session", req.cookies.get("session")?.value); // Preserve cookies
return response;
}5.3 Apollo Client 3+ Specific Considerations#
Apollo Client 3+ requires explicit defaultOptions for query policies. Ensure fetchPolicy doesn’t bypass the network (which skips cookie transmission):
const client = new ApolloClient({
// ... other config
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network', // Ensures fresh data (with cookies)
},
},
});6. Conclusion#
Cookies not being passed in Next.js + Apollo HOC configurations is a common issue, but it’s solvable with careful setup. Key takeaways:
- Configure Apollo Client with
credentials: 'include'inHttpLink. - Use
getServerSidePropsfor dynamic, user-specific data (avoids SSG’s build-time limitations). - Pass server-side cookies from
req.headers.cookieto Apollo Client in your HOC. - Fix CORS on the GraphQL server (explicit
originandcredentials: true).
By addressing these areas, you’ll ensure cookies flow reliably between the browser, Next.js, and your GraphQL server—keeping authentication and sessions working seamlessly.