NextJS使用ApolloClient调用Sanity GraphQL无法查询ID含点的文档
问题原因
- 权限缺失:你在浏览器中使用Sanity GraphQL Playground时,由于你已经登录了Sanity管理后台,浏览器会自动携带你的账号凭证发起请求,因此可以获取到所有文档,包括草稿(
drafts.前缀)和本地化衍生文档(i18n.前缀)。而前端Apollo Client发起请求时未携带授权凭证,Sanity API默认只会返回不需要权限的公开发布基础文档,也就是你看到的_id不带点的内容。 - 缓存主键冲突:如果添加凭证后仍然出现带点_id的文档丢失,是因为Apollo Client的InMemoryCache默认主键生成规则对带特殊符号的_id识别异常,不同_id的文档被判定为同一缓存键,后续返回的文档会覆盖之前的内容,最终只保留基础版本文档。
解决方法
1. 配置请求授权凭证
首先在Sanity管理后台「设置→API」页面生成一个拥有Viewer权限的只读Token,然后修改Apollo Client配置,给请求添加授权头。如果是Next.js项目,建议将Token存在服务端环境变量中,避免前端泄露:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'; import { setContext } from '@apollo/client/link/context'; const httpLink = createHttpLink({ uri: 'https://<sanity-id>.api.sanity.io/v1/graphql/test/default', credentials: 'same-origin', }); const authLink = setContext((_, { headers }) => { // 替换为你的Sanity Token,Next.js环境可通过process.env读取服务端环境变量 const sanityToken = '你的Sanity只读Token'; return { headers: { ...headers, authorization: sanityToken ? `Bearer ${sanityToken}` : '', } } }); function createApolloClient() { return new ApolloClient({ ssrMode: typeof window === 'undefined', link: authLink.concat(httpLink), cache: new InMemoryCache(), }); }
如果你的项目允许公开访问所有文档(包括草稿和本地化内容),也可以直接在Sanity后台的数据集权限设置中,将公开访问权限调整为可读取所有文档,不需要额外配置Token。
2. 手动配置缓存规则(可选)
如果添加授权后仍有文档丢失,手动指定缓存主键生成规则,避免冲突:
import { InMemoryCache, defaultDataIdFromObject } from '@apollo/client'; const cache = new InMemoryCache({ dataIdFromObject: (obj) => { if (obj.__typename && obj._id) { // 直接用完整的类型名+_id作为缓存键 return `${obj.__typename}:${obj._id}`; } return defaultDataIdFromObject(obj); } })
3. 按需过滤文档(可选)
如果你不需要返回草稿内容,可以在GraphQL查询中添加筛选条件,过滤掉drafts.前缀的草稿文档:
query test { allPage(filter: { _id: { not: { matches: "drafts.*" } } }) { title i18n_lang _id } }
内容的提问来源于stack exchange,提问作者Kim Vu
相关产品推荐
相关产品推荐

