如何通过Apollo在SSR场景下实现JSON静态文件的服务端拉取
问题答复
首先直接给结论:不能直接用原生useQuery钩子以SSR模式拉取纯JSON静态文件。useQuery的底层逻辑强绑定GraphQL协议规范,会默认校验传入的查询语句是合法GraphQL AST、返回结果匹配对应Schema结构,纯静态JSON返回的内容不满足这套校验规则,直接调用会触发解析报错,就算硬改配置绕开校验也属于工具误用,后续维护成本极高。
另外先纠正一个常见认知偏差:原生fetch并非只能在客户端执行,当前所有主流SSR运行时(Node.js 18+、Edge Runtime、各家SSR框架的服务端执行环境)都原生支持fetch API,完全可以在服务端渲染阶段直接调用fetch拉取资源,不需要等到客户端hydration阶段再执行。
可行实现方案
方案1:适配Apollo链路,复用useQuery调用习惯
如果你希望所有数据请求统一走Apollo的缓存、SSR注水逻辑,不新增额外的请求范式,可以通过自定义Apollo Link的方式把静态JSON请求整合进现有流程:
- 给静态JSON请求加独立的上下文标记,调用
useQuery时通过context字段标识这是静态资源请求,同时传入静态JSON的路径 - 编写自定义终止链路(Terminating Link),识别到静态请求标记时,直接调用fetch拉取对应JSON文件,返回符合Apollo响应格式的结果,跳过后续GraphQL解析、Schema校验流程
- 这种方式下静态JSON请求会和普通GraphQL请求一样,自动被Apollo的SSR收集逻辑捕获,完成服务端预取、客户端注水、缓存全流程复用,和你平时用
useQuery的体验没有区别
核心实现示例:
import { ApolloLink, Observable, InMemoryCache, ApolloClient, gql, useQuery } from '@apollo/client'; // 自定义静态JSON处理链路 const staticJsonFetchLink = new ApolloLink((operation, forward) => { const { fetchStaticJson, jsonPath } = operation.getContext(); if (fetchStaticJson) { return new Observable(observer => { fetch(jsonPath) .then(res => { if (!res.ok) throw new Error(`Static JSON load failed: ${res.status}`); return res.json(); }) .then(data => { observer.next({ data }); observer.complete(); }) .catch(err => observer.error(err)); }); } // 普通GraphQL请求走原有http链路 return forward(operation); }); // 初始化Client时把自定义链路放到http链路前面 const client = new ApolloClient({ link: staticJsonFetchLink.concat(httpLink), cache: new InMemoryCache(), ssrMode: typeof window === 'undefined', }); // 组件内调用示例 const STATIC_JSON_DUMMY_QUERY = gql`query StaticJsonPlaceholder { __typename }`; function YourComponent() { const { data, loading } = useQuery(STATIC_JSON_DUMMY_QUERY, { context: { fetchStaticJson: true, jsonPath: '/static/your-target-file.json' } }); // 后续使用data逻辑和普通useQuery完全一致 }
方案2:用SSR框架原生数据加载能力(生产环境优先推荐)
如果不想侵入Apollo的默认配置,直接用你所用SSR框架自带的服务端数据加载能力是最稳妥的选择,没有额外兼容成本:
- Next.js Pages Router:在
getServerSideProps(动态SSR)或getStaticProps(构建时静态生成)阶段直接用fetch拉取JSON,将结果作为props传入组件,渲染时数据已经准备完成,自动注入HTML - Next.js App Router:直接在服务端组件中
await fetch(你的静态JSON路径)即可,框架会自动处理缓存、SSR渲染逻辑 - Remix/Nuxt/其他SSR框架:对应使用框架提供的服务端loader(Remix)、
useAsyncData(Nuxt)等官方数据加载能力,这些能力都原生支持服务端执行fetch,不需要依赖Apollo即可实现SSR阶段拉取静态JSON
避坑提示
不要在客户端组件的useEffect里拉取静态JSON,这种方式会导致SSR输出的HTML缺失对应内容,等客户端hydration完成后才会触发请求渲染,很容易出现布局偏移、SEO内容缺失的问题。
内容的提问来源于stack exchange,提问作者Dujard
相关产品推荐
相关产品推荐

