网站搜索

typescript - GraphQL Apollo 客户端在 Next.js 中拒绝不使用缓存


Apollo Client 在任何情况下都拒绝用服务器的数据更新缓存的数据。我们在 Next.js (14.2.5) 的 SSR 中使用下面的 getClient() 方法运行 Apollo Client:

import { HttpLink } from "@apollo/client";
import {
  registerApolloClient,
  ApolloClient,
  InMemoryCache,
} from "@apollo/experimental-nextjs-app-support";

export const { getClient } = registerApolloClient(() => {
  return new ApolloClient({
    link: new HttpLink({
      uri: `${process.env.NEXT_PUBLIC_ADMIN_BASE_URI}/api/graphql`,
    }),
    defaultOptions: {
      query: {
        fetchPolicy: "network-only",
      },
    },
    ssrMode: true,
    ssrForceFetchDelay: 1,
    cache: new InMemoryCache({
      resultCaching: false,
      typePolicies: {
        Yachts: {
          merge: true,
          fields: {
            keyFeatures: {
              merge(existing = [], incoming: any[], { readField }) {
                const merged: any[] = existing ? existing.slice(0) : [];
                const featureNameToIndex: Record<string, number> = {};

                // Populate merged array and map from existing features
                existing.forEach((feature: any, index: any) => {
                  const name = readField<string>("name", feature);
                  if (name) {
                    featureNameToIndex[name] = index;
                  }
                });

                // Merge or add incoming features
                incoming.forEach((feature) => {
                  const name = readField<string>("name", feature);
                  if (name) {
                    const index = featureNameToIndex[name];
                    if (typeof index === "number") {
                      // Merge existing and incoming feature data
                      merged[index] = {
                        ...merged[index],
                        ...feature,
                      };
                    } else {
                      // Add new feature
                      featureNameToIndex[name] = merged.length;
                      merged.push(feature);
                    }
                  }
                });

                return merged;
              },
            },
          },
        },
      },
    }),
    /*defaultOptions: {
      watchQuery: {
        fetchPolicy: "no-cache",
        errorPolicy: "ignore",
      },
      query: {
        fetchPolicy: "no-cache",
        errorPolicy: "all",
      },
      mutate: {
        fetchPolicy: "no-cache",
      },
    },*/
  });
});

然而,对我们的 API 发起查询,例如下面的示例(通过 Postman 验证,其数据更新是正确的),将返回不是最新的数据:

"use server";

import IBrokerino from "@/types/brokerino";
import { getClient } from "@/apollo";
import { gql } from "@apollo/client";

export const fetchBrokerinos = async (): Promise<IBrokerino[]> => {
  const client = getClient();
  const { data } = await client.query({
    query: gql`
      query Users {
        Users(where: { displayOnWebsite: { equals: true } }) {
          docs {
            name
            picture {
              alt
              sizes {
                fhd {
                  url
                  width
                  height
                }
              }
            }
            phones {
              prefix
              number
            }
            langs
            email
            position
          }
        }
      }
    `,
  });
  return data.Users.docs;
};

我们也尝试过将每个 query 的单独 fetch-policy 设置为 no-cache,但情况没有改变。

我们需要去除任何缓存,因为我们的应用程序需要在每次网站加载时都从 API 获取数据进行查询。我们也尝试过切换不同的客户端或在 Next.js 中禁用缓存,但都没有效果。

这是我们在 page.tsx 中进行服务器端查询的方式:

import Hero from "@/components/company/hero";
import Bar from "@/components/nav/bar";
import dynamic from "next/dynamic";
import { fetchBrokerinos } from "@/actions/brokerino";

const View = dynamic(() => import("@/components/view"));
const Footer = dynamic(() => import("@/components/footer"));
const Story = dynamic(() => import("@/components/company/story"));
const Accordion = dynamic(() => import("@/components/company/accordion"));
const Team = dynamic(() => import("@/components/company/team"));
const Lifestyle = dynamic(() => import("@/components/company/lifestyle"));

const Company = async () => {
  return (
    <main className="w-full flex flex-col justify-start items-center">
      <Bar dynamicColor={-1} />
      <View />
      <Hero />
      <Story />
      <Accordion />
      <Team brokerinos={await fetchBrokerinos()} />
      <Lifestyle />
      <Footer />
    </main>
  );
};

export default Company;

答案#1

这不是 Apollo Client 缓存你的请求,而是 Next.js 缓存发送出去的 fetch 调用。

请查看 Next.js 关于 fetch 缓存的文档

你需要设置 fetchOptions: { cache: "no-store" } 来告诉 Next.js 停止那样做。来自 @apollo/experimental-nextjs-app-support 包的 README 的这个示例显示了这一点:

export const { getClient, query, PreloadQuery } = registerApolloClient(() => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: new HttpLink({
      // this needs to be an absolute url, as relative urls cannot be used in SSR
      uri: "http://example.com/api/graphql",
      // you can disable result caching here if you want to
      // (this does not work if you are rendering your page with `export const dynamic = "force-static"`)
      fetchOptions: { cache: "no-store" },
    }),
  });
});

请移除那些

    ssrMode: true,
    ssrForceFetchDelay: 1,

他们对流式 SSR 什么也不做。你也不需要 resultCaching: false,,可能其他大多数选项也不用。