GraphQL服务本地正常但在Vercel生产环境运行异常求助
本地开发代码(server.js)
import { ApolloServer } from "apollo-server"; import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core"; import typeDefs from './logic/schemaGQL.js'; import mongoose from "mongoose"; import jwt from 'jsonwebtoken'; import dotenv from 'dotenv'; dotenv.config(); const MONGO_URI = process.env.DATABASE_URL; const SECRATE = process.env.SECRATE; mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }) mongoose.connection.on("connected", () => { console.log("connected to mongodb") }) mongoose.connection.on("error", (err) => { console.log("error connecting", err) }) import './DB/User.js' import './DB/SubWaiting.js' import './DB/Sub.js' import './DB/Msg.js' import './DB/SubUncheck.js' import './DB/Revenue.js' import './DB/Month.js' import './DB/PlanSold.js' import './DB/Waiting.js' import './DB/Refund.js' import './DB/Help.js' import resolvers from './logic/resolver.js' const context = ({ req }) => { const { authorization } = req.headers; if (authorization) { const { userId } = jwt.verify(authorization, SECRATE) return { userId } } } const server = new ApolloServer({ typeDefs, resolvers, context, plugins: [ ApolloServerPluginLandingPageGraphQLPlayground() ] }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
Vercel生产环境代码(server.js)
import { ApolloServer } from 'apollo-server-micro'; import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core'; import typeDefs from './logic/schemaGQL.js'; import mongoose from "mongoose"; import jwt from 'jsonwebtoken'; import dotenv from 'dotenv'; dotenv.config(); const MONGO_URI = process.env.DATABASE_URL; const SECRATE = process.env.SECRATE; mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true, }); mongoose.connection.on("connected", () => { // console.log("Connected to MongoDB"); }); mongoose.connection.on("error", (err) => { // console.log("Error connecting to MongoDB", err); }); import resolvers from './logic/resolver.js'; const context = ({ req }) => { const { authorization } = req.headers; if (authorization) { const { userId } = jwt.verify(authorization, SECRATE); return { userId }; } }; const server = new ApolloServer({ typeDefs, resolvers, context, plugins: [ ApolloServerPluginLandingPageGraphQLPlayground(), ], }); // const startServer = server.start(); export default server.createHandler({ path: '/api/graphql', }); export const config = { api: { bodyParser: false, }, };
vercel.json配置
{ "version": 2, "builds": [{ "src": "server.js", "use": "@vercel/node" }], "routes": [{ "src": "/api/graphql", "dest": "/api/graphql" }] }
尝试过的路由修改
"routes": [{ "src": "/(.*)", "dest": "/api" }]
问题现象
访问 https://splits-two.vercel.app/api/graphql 时,偶尔出现404 Not Found、500 Internal Server Error,或是无服务器崩溃错误,本地运行完全正常。
可能的原因及修复方案
1. Apollo Server启动时机错误
你注释掉了server.start(),但Vercel的无服务器函数环境中,Apollo Server必须先完成启动才能处理请求。直接调用createHandler()会导致服务器未初始化就接收请求,引发500或崩溃。
修复:取消注释启动代码,改为异步等待启动完成后再处理请求:
const startServer = server.start(); export default async function handler(req, res) { await startServer; return server.createHandler({ path: '/api/graphql' })(req, res); }
2. 环境变量未正确配置
本地用dotenv加载.env文件,但Vercel生产环境不会读取本地.env,必须在控制台手动配置DATABASE_URL和SECRATE。如果变量缺失或错误,会直接导致MongoDB连接失败、JWT验证失败,触发500错误。
修复:进入Vercel项目的「Settings -> Environment Variables」,添加这两个变量,确保值和本地一致。
3. Vercel路由配置错误
你当前的路由配置中,dest指向/api/graphql,但实际server.js是作为无服务器函数部署的,正确的dest应该指向函数文件路径/server.js。之前尝试的通配路由会把所有请求转发到/api,和你的GraphQL路径不匹配,导致404。
修复:修改vercel.json路由:
{ "version": 2, "builds": [{ "src": "server.js", "use": "@vercel/node" }], "routes": [{ "src": "/api/graphql(.*)", "dest": "/server.js" }] }
4. MongoDB连接问题
- 旧的连接选项
useNewUrlParser、useUnifiedTopology在新版本mongoose中已经是默认值,保留可能引发兼容性问题,建议移除。 - 无服务器环境中,数据库连接随函数冷启动建立,频繁启停可能导致连接池异常,建议改用异步连接并捕获错误:
修复:
try { await mongoose.connect(MONGO_URI); console.log("Connected to MongoDB"); } catch (err) { console.error("MongoDB connection failed:", err); throw err; }
5. Context函数的潜在报错
如果请求没有authorization头,context函数会返回undefined,依赖userId的resolver可能抛出错误,导致500。
修复:默认返回空对象或null值:
const context = ({ req }) => { const { authorization } = req.headers; if (authorization) { try { const { userId } = jwt.verify(authorization, SECRATE); return { userId }; } catch (err) { // 处理无效token return { userId: null }; } } return { userId: null }; };
内容的提问来源于stack exchange,提问作者Umesh Ola

