Next.js 14:如何在SSR组件中调用GraphQL查询(无需"use client")
实现Next.js SSR产品页(无需"use client")获取GraphQL数据
背景
已完成Next.js项目搭建并配置Apollo Client,RootLayout中通过ApolloProviderClient包裹子组件。现需创建产品页,要求通过SSR渲染所有产品以满足SEO需求,且不使用"use client"指令。
解决方案
Next.js App Router中默认组件为服务器组件,可直接调用Apollo Client的client.query()方法在服务器端获取GraphQL数据,无需依赖客户端hooks(如useQuery),自然无需"use client"。
步骤1:调整Apollo Client的授权逻辑
原Client代码中使用了浏览器专属的sessionStorage/localStorage,服务器端无法访问,需修改为区分环境的token获取逻辑:
// @/utils/apollo-clients.ts import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; import { baseUrl } from "@/utils/constant/baseUrl"; import { cookies } from "next/headers"; const httpLink = createHttpLink({ uri: baseUrl, }); const authLink = setContext((_, { headers }) => { let token = ""; // 区分服务器/客户端环境 if (typeof window === "undefined") { // 服务器端从Cookie获取token const cookieStore = cookies(); token = cookieStore.get("accessToken")?.value || ""; } else { // 客户端沿用原有逻辑 const sessionToken = sessionStorage.getItem("accessToken"); const localToken = localStorage.getItem("accessToken"); token = sessionToken || localToken || ""; } return { headers: { ...headers, Authorization: token ? `Bearer ${token}` : "", }, }; }); export const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), });
步骤2:定义GraphQL查询语句
创建查询文件存放产品列表的GraphQL查询:
// @/queries/products.ts import { gql } from "@apollo/client"; export const GET_PRODUCTS = gql` query GetProducts { products { id name description price # 按需添加其他字段 } } `;
步骤3:创建SSR产品页(服务器组件)
在app/products/page.tsx创建服务器组件,直接调用Apollo Client获取数据并渲染:
// app/products/page.tsx import { client } from "@/utils/apollo-clients"; import { GET_PRODUCTS } from "@/queries/products"; // 默认服务器组件,无需"use client" export default async function ProductsPage() { // 服务器端异步获取产品数据 const { data, error } = await client.query({ query: GET_PRODUCTS, // 如需传参可添加variables: { category: "electronics" }等 }); // 处理错误 if (error) { return <div>产品加载失败:{error.message}</div>; } const products = data.products; return ( <div className="products-container"> <h1>全部产品</h1> <div className="products-grid"> {products.map((product) => ( <div key={product.id} className="product-card"> <h3>{product.name}</h3> <p>{product.description}</p> <span className="price">${product.price}</span> </div> ))} </div> </div> ); }
关键说明
- 该组件为服务器组件,数据在服务器端预获取后直接渲染为HTML,完全满足SEO需求,无需客户端 hydration 即可展示内容。
- 服务器组件中无需包裹ApolloProvider,直接使用Apollo Client实例完成数据查询。
- 调整后的Client逻辑兼容服务器/客户端环境,避免服务器端因访问浏览器API报错。
内容的提问来源于stack exchange,提问作者Asit singh
相关产品推荐
相关产品推荐

