Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: could not identify unique query #386

Open
dameradev opened this issue Nov 7, 2024 · 3 comments
Open

Error: could not identify unique query #386

dameradev opened this issue Nov 7, 2024 · 3 comments

Comments

@dameradev
Copy link

Hello, we recently updated to "@apollo/experimental-nextjs-app-support"; We need this to run server queries.

The problem is that now our global store queries are throwing the error below when on first page visit/refresh.





Versions

"@apollo/client": "^3.11.8",
"@apollo/experimental-nextjs-app-support": "^0.11.5",



This is the error we're getting

 const { serverQuery } = queryManager.getDocumentInfo(transformedDocument);
  1540 |     if (!serverQuery) {
> 1541 |       throw new Error("could not identify unique query");
       | ^
  1542 |     }
  1543 |     const canonicalVariables = canonicalStringify(options.variables || {});
  1544 |     const cacheKeyArr = [print(serverQuery), canonicalVariables];



Below is how we've setup the apollo wrapper


"use client";
import { HttpLink } from "@apollo/client";
import {
  ApolloNextAppProvider,
  ApolloClient,
} from "@apollo/experimental-nextjs-app-support";
import { getApiEndPoint } from "src/config";
import cache from '../src/lib/apollo/storage/cache';

// have a function to create a client for you
function makeClient(token) {
  const httpLink = new HttpLink({

    // this needs to be an absolute url, as relative urls cannot be used in SSR
    uri: getApiEndPoint(),
    headers: {
      authorization: `Bearer ${typeof window !== "undefined" ? localStorage.getItem("token") : token}`,
    },
    // 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.:
  });
  
  // use the `ApolloClient` from "@apollo/experimental-nextjs-app-support"
  return new ApolloClient({
    // use the `InMemoryCache` from "@apollo/experimental-nextjs-app-support"
    // fetchOptions: { cache: "no-store" },
    cache,
    link: httpLink,
  });
}

// you need to create a component to wrap your app in
export function ApolloWrapper({ children, cookieToken }: { children: React.ReactNode, cookieToken: string }) {

  const client = makeClient(cookieToken); // Create Apollo client instance
  return (
    <ApolloNextAppProvider makeClient={() => client}>
      {children}
    </ApolloNextAppProvider>
  );
}



Here's the query that's erroring out (one of them, all global store queries are erroring out)

import { useQuery } from "@apollo/client";
import { GET_USER_STORE } from "../../queries";
import { storeVar } from "./apolloVars";


export default function useStore() {
  const { data } = useQuery(GET_USER_STORE, {
    fetchPolicy: "cache-only",
  });

  const store = data?.store;

  return {
    name: store?.name,
    address: store?.address,
    line_1: store?.line_1,
    city: store?.city,
    zipCode: store?.zipCode,
    country: store?.country,
    warehouseId: store?.warehouseId,
    external_ref: store?.external_ref,
    basePrice: store?.basePrice,
    id: store?.id,
    termsAndConditionsAccepted: store?.termsAndConditionsAccepted,
    users: store?.users,
    priority: store?.priority,
    customerDefinition: store?.customerDefinition,
    warehouseName: store?.warehouseName,
    currency: store?.currency,
    salesperson: store?.salesperson,
    status: store?.status,

    addStore({
      name,
      address,
      warehouseId,
      external_ref,
      basePrice,
      line_1,
      city,
      zipCode,
      country,
      id,
      termsAndConditionsAccepted,
      users,
      priority,
      customerDefinition,
      warehouseName,
      currency,
      salesperson,
      status
    }) {

      storeVar({
        name : name || store?.name, 
        address: address || store?.address,
        warehouseId: warehouseId || store?.warehouseId,
        country: country || store?.country,
        external_ref: external_ref || store?.external_ref,
        basePrice: basePrice || store?.basePrice,
        line_1: line_1 || store?.line_1,
        city: city || store?.city,
        zipCode : zipCode || store?.zipCode,
        id : id || store?.id,
        termsAndConditionsAccepted : termsAndConditionsAccepted || store?.termsAndConditionsAccepted,
        users : users || store?.users,
        priority : priority || store?.priority,
        customerDefinition : customerDefinition || store?.customerDefinition,
        warehouseName : warehouseName || store?.warehouseName,
        currency : currency || store?.currency,
        salesperson : salesperson || store?.salesperson,
        status: status || store?.status,
      });
    },
  };
}





Here's how it's all defined



export const GET_USER_STORE = gql`
  query GET_USER_STORE {
    store @client 
  }
`;
export const storeVar = makeVar<Store>(InitalStore);

@phryneas
Copy link
Member

phryneas commented Nov 7, 2024

I just commented in the forum, but let's continue the conversation here:

We cannot stream queries from the server to the client that contain only client-local fields.

Also keep in mind that makeVar has the risk here to share data between all your users during SSR. It's like a global variable.

@dameradev
Copy link
Author

I just commented in the forum, but let's continue the conversation here:

We cannot stream queries from the server to the client that contain only client-local fields.

Also keep in mind that makeVar has the risk here to share data between all your users during SSR. It's like a global variable.

So what's the solution here?

How can I make sure to stream these queries only on the client?

@phryneas
Copy link
Member

phryneas commented Nov 7, 2024

I'm afraid, you probably won't be able to use makeVar, just like any other global state solution, with a SSR approach like Next.js at all.

Even if we could solve the "data of multiple users is getting mixed up" problem, every future render on the server would not know what data the user has set in the browser, so it would render "initial data" again.


If you can ensure that all of this will only be accessed in the browser (keep in mind that Next.js SSRs your Client Component, too!), I would recommend skipping Apollo Client here and calling useReactiveVar instead of useQuery/useSuspenseQuery.

That said, this is a very strong if. You probably would have to avoid rendering whole subtrees of your app on the server, with something like

const isOnServer = useSyncExternalStore(() => {}, () => false, () => true)

and then conditionally rendering children or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants