/** * Apollo Client(v2.1 M8 验收点) * * 所有 portal-shell 查询走 apollo-router(GraphQL 联邦入口): * portal-shell → apollo-router :3000/graphql → 各子图(iam/core-edu/content/msg/data-ana/ai/config-service) * * 双端使用: * - 服务端(RSC):createApolloClient() 每次请求新建实例,ssrMode=true * - 客户端:getApolloClient() 单例,复用 InMemoryCache * * 关联:portal-shell spec §5.5 RSC 预取、§5.6 统一 Hook、M8 验收标准 */ import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; const APOLLO_ROUTER_URL = process.env.NEXT_PUBLIC_APOLLO_ROUTER_URL || process.env.APOLLO_ROUTER_URL || "http://localhost:3000/graphql"; /** * 创建 Apollo Client 实例。 * * @param getAuthToken 可选,返回 JWT 用于注入 Authorization 头(客户端从 cookie/localStorage 读取) */ export function createApolloClient( getAuthToken?: () => string | null, ): ApolloClient { const httpLink = new HttpLink({ uri: APOLLO_ROUTER_URL, credentials: "include", }); const authLink = setContext((_, { headers }) => { const token = getAuthToken?.() ?? null; return { headers: { ...headers, ...(token ? { Authorization: `Bearer ${token}` } : {}), }, }; }); return new ApolloClient({ link: from([authLink, httpLink]), cache: new InMemoryCache(), ssrMode: typeof window === "undefined", defaultOptions: { query: { errorPolicy: "all", fetchPolicy: typeof window === "undefined" ? "no-cache" : "cache-first", }, watchQuery: { errorPolicy: "all", }, }, }); } let clientSingleton: ApolloClient | null = null; /** * 获取客户端 Apollo Client 单例(浏览器侧复用缓存)。 */ export function getApolloClient( getAuthToken?: () => string | null, ): ApolloClient { if (typeof window === "undefined") { return createApolloClient(getAuthToken); } if (!clientSingleton) { clientSingleton = createApolloClient(getAuthToken); } return clientSingleton; }