apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.38k stars 2.66k forks source link

NextJS with "useSuspenseQuery" not passing headers to request #11868

Closed EfstathiadisD closed 4 months ago

EfstathiadisD commented 5 months ago

Issue Description

I have a NextJS 14 Application on Pages Router. I have a Query run with useSuspendQuery, which doesn't pass the headers: { Cookie: "" } from the context. Both SSR and CSR Queries and Mutations work like a charm. How is Suspended Query different?

To verify the issue, I passed the Cookie as a hardcoded value to context: { headers: { Cookie: "VALID_VALUE" } } and it worked, but according to the docs it should read the default context. I guess that's the real culprit here.

Link to Reproduction

Codesanbox

Reproduction Steps

FYI: The reproduction isn't representable cause no requests are made. But my setup is pretty simple and aside from the suspenseQuery everything else work like a charm.

Here is some code I have for reference:

import type { NormalizedCacheObject, QueryOptions } from "@apollo/client";
import { ApolloClient, HttpLink, InMemoryCache, from } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { onError } from "@apollo/client/link/error";
import type { Context, Middleware } from "@workspace/types";

import { isNotNull } from "@workspace/utils";

const isBrowser = typeof window !== "undefined";

type GraphQLClient = typeof apolloClient;
let apolloClient: ApolloClient<NormalizedCacheObject> | null = null;

const API_URI = isBrowser
  ? process.env["NEXT_PUBLIC_API_HOST"]
  : process.env["NEXT_INTERNAL_API_HOST"];

/* eslint-disable no-console */
function createLink(props: { context?: QueryOptions["context"] }) {
  const httpLink = new HttpLink({ uri: API_URI, credentials: "include" });

  const authLink = props.context
    ? setContext((_, { headers }) => ({
        headers: {
          ...headers,
          ...props.context?.headers,
        },
      }))
    : null;

  const errorLink = onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors) {
      graphQLErrors.forEach(({ message, path, extensions }) => {
        if (extensions?.code === "UNAUTHENTICATED") {
          return;
        }

        console.error("[GraphQL error]:", { message, path });
      });
    }

    if (networkError) {
      console.log("[Network error]:", networkError);
    }
  });

  const links = [errorLink, authLink, httpLink].filter(isNotNull);

  return from(links);
}

type CreateClientProps = {
  context?: QueryOptions["context"];
};

function createClient(props: CreateClientProps) {
  const link = createLink({
    context: props.context,
  });

  return new ApolloClient({
    link,
    ssrMode: !isBrowser,
    cache: new InMemoryCache(),
  });
}

function initApollo(props: Omit<CreateClientProps, "uri"> = {}) {
  if (!isBrowser) {
    return createClient({ ...props });
  }

  if (!apolloClient) {
    apolloClient = createClient({ ...props });
    return apolloClient;
  }

  return apolloClient;
}

function withApollo<T extends Context>(
  fn: Middleware<T, { graphql: ReturnType<typeof initApollo> }>,
) {
  return (ctx: T) => {
    const graphql = initApollo({
      context: {
        headers: {
          Cookie: ctx.req.headers.cookie,
        },
      },
    });

    return fn({ ...ctx, graphql });
  };
}

export type { GraphQLClient };
export { initApollo, withApollo };

I use the above to init the client with the provider and using the withApollo for SSR requests. But the suspense one doesn't work. it doesn't pass the headers. The setup is identical to what I have in the repro just with actual calls..

@apollo/client version

3.10.4

UPDATE

I checked with the latest versions as well as 3.8.10 which was the previous one i was using in another repo and still doesn't work. The issue seems to be present in the App Dir as well, but I don't think it has anything to do with NextJS tbh, but could be wrong.

phryneas commented 5 months ago

Hi Efstathiadis,

the CodeSandbox is set to private, could you please make it public?

EfstathiadisD commented 5 months ago

Hi Efstathiadis,

the CodeSandbox is set to private, could you please make it public?

Sorry for that. I think it should be public now..

phryneas commented 5 months ago

I have to admit that I'm a bit confused what the Codesandbox is showing. It doesn't seem like you're setting any headers anywhere in the CodeSandbox.

Could you explain a bit what I should look for here to see this reproduced?

github-actions[bot] commented 4 months ago

We're closing this issue now but feel free to ping the maintainers or open a new issue if you still need support. Thank you!

github-actions[bot] commented 3 months ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. For general questions, we recommend using StackOverflow or our discord server.