页面加载时GraphQL 400错误排查(KeystoneJS+NextJS+Apollo)
节日快乐!看起来你遇到的问题是KeystoneJS的Apollo helpers组件在内部发送了一个不符合GraphQL规范的查询,导致出现400验证错误——虽然你自己写的查询最终成功了,但这些额外的错误请求还是挺闹心的。咱们一步步来排查解决:
问题根源分析
从错误信息里的提示Field "queries" of type "_ListQueries" must have a selection of subfields就能看出来:你并没有主动请求queries字段,但这个请求确实发生了。这大概率是@keystonejs/apollo-helpers里的KeystoneProvider或Query组件在初始化过程中,自动发送了一个内部默认查询,而这个查询没有指定queries字段的子选择项,触发了GraphQL的验证规则。
同时你在代码里同时用了@apollo/client的ApolloProvider和Keystone自己的KeystoneProvider,两者的客户端实例可能存在冲突,进一步加剧了这个问题。
解决方案步骤
1. 移除重复的Apollo Provider配置
KeystoneProvider已经内置了Apollo客户端的上下文管理,不需要再单独实例化ApolloClient和添加ApolloProvider。试试修改你的_app.tsx:
import { useReducer } from "react"; import { useRouter } from "next/router"; import { ThemeProvider } from "styled-components"; import { Query, KeystoneProvider } from "@keystonejs/apollo-helpers"; import { gql } from "@apollo/client"; import { primaryTheme } from "../styles/theme"; import GlobalStyle from "../styles/global"; import { initialState, globalReducer } from "../context/reducer"; import Meta from "../components/global/Meta"; import { Header } from "../components/global/Header"; import { globalContext } from "../context/contex"; import Footer from "../components/global/Footer"; import Loading from "../components/global/Loading"; const QUERY = gql` query { allOperations{ id name } } ` const MyApp = ({ Component, pageProps }) => { const [store, dispatch] = useReducer(globalReducer, initialState); const router = useRouter(); return ( <KeystoneProvider clientOptions={{ uri: process.env.NEXT_PUBLIC_API_URL, fetchOptions: { mode: 'cors' } }}> <ThemeProvider theme={primaryTheme}> <Meta /> {router.route !== "/" && <Header />} <Query query={QUERY} > {({ loading, data }) => { console.log(data); if(loading){ return <Loading /> } else { return <Component {...pageProps} /> }}} </Query> <Footer /> <GlobalStyle /> </ThemeProvider> </KeystoneProvider> ); }; export default MyApp;
这里直接通过KeystoneProvider的clientOptions配置API地址,不再手动创建ApolloClient和添加ApolloProvider,避免双实例冲突。
2. 替换Keystone的Query组件为官方Apollo版本
如果上面的方法没效果,建议放弃使用@keystonejs/apollo-helpers的Query组件,改用@apollo/client的官方Query组件,完全掌控查询逻辑:
// 统一导入@apollo/client的组件 import { ApolloProvider, ApolloClient, InMemoryCache, HttpLink, gql, Query } from "@apollo/client"; // 移除@keystonejs/apollo-helpers的Query和KeystoneProvider const client = new ApolloClient({ ssrMode: true, cache: new InMemoryCache(), link: new HttpLink({ uri: process.env.NEXT_PUBLIC_API_URL, fetchOptions: { mode: 'cors' }, }), }); // 其他代码不变,只保留ApolloProvider const MyApp = ({ Component, pageProps }) => { const [store, dispatch] = useReducer(globalReducer, initialState); const router = useRouter(); return ( <ApolloProvider client={client}> <ThemeProvider theme={primaryTheme}> <Meta /> {router.route !== "/" && <Header />} <Query query={QUERY} > {({ loading, data }) => { console.log(data); if(loading){ return <Loading /> } else { return <Component {...pageProps} /> }}} </Query> <Footer /> <GlobalStyle /> </ThemeProvider> </ApolloProvider> ); };
这种方式完全使用官方Apollo Client,避免Keystone内部的默认查询干扰。
3. 升级KeystoneJS版本
这个错误很可能是旧版本@keystonejs/apollo-helpers的bug,试试升级Keystone相关依赖到最新稳定版:
npm update @keystonejs/keystone @keystonejs/apollo-helpers @keystonejs/app-graphql
4. 验证GraphQL Schema
确保你的Operation列表在Keystone中正确定义,allOperations查询确实存在并且包含id和name字段。你可以访问http://localhost:3000/admin/graphiql(Keystone自带的GraphiQL界面)手动执行你的查询,确认是否能正常返回结果。
额外说明
你提到前两次请求返回204,最后一次返回200,这说明你自己写的查询最终是成功的,前面的错误请求完全来自Keystone内部的默认操作。通过上面的步骤应该可以彻底消除这些错误请求,让页面加载更顺畅。
内容的提问来源于stack exchange,提问作者Bora ALAP

