Next.js 13+Urql 3静态生成中Guest Token获取问题求助
问题解答
1. 改用Cookie存储Token能否解决?
能。核心原因:
sessionStorage是浏览器专属的本地存储,服务端(包括getStaticProps的执行环境)完全无法访问浏览器的存储对象,所以服务端执行查询时拿不到Token。- Cookie会随每次HTTP请求自动携带到服务端,Next.js中可以通过服务端API(比如
cookies())直接读取Cookie里的Token,之后就能在Urql请求中带上这个Token完成鉴权。
2. 是否是Urql配置问题?
大概率是。Urql的客户端初始化需要区分服务端环境和客户端环境:
- 客户端可以直接从Cookie/sessionStorage取Token,但服务端必须从请求携带的Cookie中获取Token,再注入到Urql的请求头里。如果你的Urql配置没做这种差异化处理,服务端发起的请求就不会带上Token,自然查询失败。
修正后的代码示例
1. 迁移Token存储到Cookie(客户端设置)
在客户端获取到Guest Token后,替换原sessionStorage存储逻辑为Cookie:
// 客户端代码,比如获取到Guest Token后 import { setCookie } from 'cookies-next'; const guestToken = '获取到的访客Token'; // 设置Cookie,确保服务端能读取到 setCookie('guest-token', guestToken, { path: '/', maxAge: 86400, // 有效期1天 httpOnly: false, // 若需要客户端也能读取则设为false,仅服务端使用可设为true secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS专属 });
2. 差异化配置Urql客户端
创建适配服务端/客户端的Urql客户端工厂函数:
// lib/urql-client.ts import { createClient, dedupExchange, fetchExchange, Exchange } from 'urql'; import { cookies } from 'next/headers'; // 服务端专属鉴权Exchange:从Cookie取Token并添加到请求头 const serverAuthExchange: Exchange = ({ forward }) => async (ops$) => { const cookieStore = cookies(); const guestToken = cookieStore.get('guest-token')?.value; return forward( ops$.map((op) => { if (!guestToken) return op; return { ...op, context: { ...op.context, fetchOptions: { ...op.context.fetchOptions, headers: { ...op.context.fetchOptions?.headers, // 替换为你的API要求的Token头字段,比如X-Guest-Token 'X-Guest-Token': guestToken, }, }, }, }; }) ); }; // 客户端专属鉴权Exchange:从Cookie取Token const clientAuthExchange: Exchange = ({ forward }) => async (ops$) => { // 从Cookie读取Token const guestToken = document.cookie .split('; ') .find(row => row.startsWith('guest-token=')) ?.split('=')[1]; return forward( ops$.map((op) => { if (!guestToken) return op; return { ...op, context: { ...op.context, fetchOptions: { ...op.context.fetchOptions, headers: { ...op.context.fetchOptions?.headers, 'X-Guest-Token': guestToken, }, }, }, }; }) ); }; export const createUrqlClient = (isServer: boolean) => { const exchanges = [dedupExchange]; // 根据环境添加对应的鉴权Exchange isServer ? exchanges.push(serverAuthExchange) : exchanges.push(clientAuthExchange); exchanges.push(fetchExchange); return createClient({ url: 'https://你的API地址/graphql', exchanges, }); };
3. 在getStaticProps中使用服务端Urql客户端
// pages/目标页面.tsx(Next.js13 Pages Router) import { createUrqlClient } from '../lib/urql-client'; import { gql } from 'urql'; // 你的GraphQL查询语句 const GET_CONTENT_QUERY = gql` query GetTargetContent { contentList { id title } } `; export async function getStaticProps() { // 创建服务端专属Urql客户端 const client = createUrqlClient(true); const { data, error } = await client.query(GET_CONTENT_QUERY, {}).toPromise(); if (error) { return { notFound: true }; } return { props: { data }, revalidate: 60, // 可选:开启增量静态再生 }; } export default function TargetPage({ data }) { return ( <div> {/* 渲染服务端获取到的数据 */} {data?.contentList?.map(item => <div key={item.id}>{item.title}</div>)} </div> ); }
额外注意事项
- 若使用Next.js13 App Router(app目录),
getStaticProps需替换为generateStaticParams+ 服务端组件逻辑,可配合@urql/next包简化服务端客户端的适配。 - Cookie的
path需设置为/,否则服务端可能无法读取到指定路径下的Cookie。
内容的提问来源于stack exchange,提问作者Sami Kalammallah
相关产品推荐
相关产品推荐

