Next.js+GraphQL+Apollo Server Micro项目报错:called start() with surprising state started及请求无响应问题求助
Fix: "called start() with surprising state started" in Next.js + Apollo Server Micro
我一眼就看出问题所在了——你的handler函数里每次请求都会调用await apolloServer.start(),但Apollo Server的start()方法只能被调用一次。Next.js的API路由在每次请求时都会重新执行整个模块的代码(尤其是开发模式下),第一次请求启动成功后,后续请求再调用start()就会触发这个"already started"的冲突错误。
具体修复方案
下面两种方法都能解决问题,选你觉得顺手的就行:
方案1:用标记变量控制启动逻辑
给Apollo Server加一个启动状态标记,确保start()只执行一次:
import { ApolloServer } from 'apollo-server-micro'; import typeDefs from '../../backend/graphql/typeDefs.js'; import resolvers from '../../backend/graphql/resolvers/index.js'; import connectDb from '../../backend/db/config/connectDb.js'; // 数据库连接也建议改成只执行一次 let dbConnected = false; const connectDbIfNeeded = async () => { if (!dbConnected) { await connectDb(); dbConnected = true; } }; connectDbIfNeeded(); // 初始化Apollo Server const apolloServer = new ApolloServer({ typeDefs, resolvers }); // Next.js API配置 export const config = { api: { bodyParser: false, // 直接写false更简洁,不需要Boolean(false) } }; // 标记Apollo Server是否已启动 let apolloServerStarted = false; const handler = async (req, res) => { if (!apolloServerStarted) { await apolloServer.start(); apolloServerStarted = true; } // 注意这里要await,确保请求处理完成 await apolloServer.createHandler({ path: '/api/graphql' })(req, res); }; export default handler;
方案2:提前在模块级别启动服务器
把启动逻辑移到模块级别,利用模块代码只会执行一次的特性(开发模式热重载时可能会重新执行,但不影响功能):
import { ApolloServer } from 'apollo-server-micro'; import typeDefs from '../../backend/graphql/typeDefs.js'; import resolvers from '../../backend/graphql/resolvers/index.js'; import connectDb from '../../backend/db/config/connectDb.js'; // 数据库连接优化 let dbConnected = false; const connectDbIfNeeded = async () => { if (!dbConnected) { await connectDb(); dbConnected = true; } }; connectDbIfNeeded(); // 初始化并提前启动Apollo Server const apolloServer = new ApolloServer({ typeDefs, resolvers }); const startServer = apolloServer.start(); // Next.js API配置 export const config = { api: { bodyParser: false, } }; const handler = async (req, res) => { // 确保服务器启动完成再处理请求 await startServer; await apolloServer.createHandler({ path: '/api/graphql' })(req, res); }; export default handler;
额外说明
- 为什么数据库连接也要优化?因为你的原代码里
connectDb()在模块级别,每次请求都会尝试连接数据库,重复连接会造成资源浪费,甚至触发数据库的连接数限制。 bodyParser: false是必须的,因为Apollo Server需要自己解析请求体,Next.js的默认bodyParser会干扰这个过程。
内容的提问来源于stack exchange,提问作者LePr3z
相关产品推荐
相关产品推荐

