Next.js API路由Mongoose查询超时:places.find()缓冲超时
解决Next.js + Mongoose查询MongoDB超时问题
问题背景
开发Next.js应用时,使用Mongoose查询MongoDB数据库,确认连接已建立,但查询始终超时,报错Operation places.find() buffering timed out after 10000ms。
相关代码
Place.js
import mongoose from 'mongoose'; const placeSchema = new mongoose.Schema({ country: String, population: Number, work: { pub: Number, pub_ratio: Number, languages: { primary: String, secondary: String, }, avg: Number, attendance: Number, }, culture: { food_type: String, climate: String, internet_penetration: String, transportation_infra: String, crime_rate: String, recreation: String, }, }); placeSchema.index({ 'culture.recreation': 1 }); const Place = mongoose.models.Place || mongoose.model('Place', placeSchema, 'places'); export default Place;
connectDB.js
import mongoose from 'mongoose'; const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/NG_DESTINATION'; if (!MONGO_URI) { throw new Error('Please define the MONGO_URI environment variable inside .env.local'); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } async function connectDB() { console.log('Checking cached connection...'); if (cached.conn) { console.log('Using cached connection'); return cached.conn; } if (!cached.promise) { console.log('No cached promise found, creating new connection promise...'); const opts = { bufferCommands: false, serverSelectionTimeoutMS: 50000, socketTimeoutMS: 50000, }; cached.promise = mongoose.connect(MONGO_URI, opts).then((mongoose) => { console.log('MongoDB connected'); return mongoose; }).catch(err => { console.error('MongoDB connection error:', err); throw err; }); } try { cached.conn = await cached.promise; console.log('MongoDB connection established'); } catch (error) { console.error('Error awaiting connection promise:', error); throw error; } return cached.conn; } export default connectDB;
match-places.js
import connectDB from '../../../lib/connectDB'; import Place from '../../../backend/models/Place'; export default async (req, res) => { if (req.method === 'POST') { try { console.log("Connecting to database..."); await connectDB(); console.log("Connected to database"); const userPreferences = req.body; console.log("User Preferences: ", userPreferences); console.log("Starting MongoDB query..."); const places = await Place.find({}).limit(10); console.log("Places found: ", places); if (places.length === 0) { console.error("No places found."); return res.status(404).json({ error: "No places found." }); } res.status(200).json({ places }); } catch (error) { console.error("Error during database operation: ", error); res.status(500).json({ error: error.message }); } } else { res.status(405).end(); // Method Not Allowed } };
index.js
import React, { useState } from 'react'; import { Box, ChakraProvider, Text } from '@chakra-ui/react'; import axios from 'axios'; import Questionnaire from '../../components/Questionnaire'; import Results from '../../components/Results'; const Home = () => { const [place, setPlace] = useState(null); const handleSubmit = async (responses) => { try { console.log("Home - Submitting Responses:", responses); const response = await axios.post('/api/match-places', responses); console.log("Home - API Response:", response.data); setPlace(response.data.recommendation); } catch (error) { console.error('Error fetching recommendation:', error); } }; return ( <ChakraProvider> <Box p={5}> <Text fontSize="3xl" fontWeight="bold">Find Your Destination</Text> <Questionnaire onSubmit={handleSubmit} /> {place && <Results place={place} />} </Box> </ChakraProvider> ); }; export default Home;
错误表现
- 前端提交表单触发500错误:
POST http://localhost:3000/api/match-places 500 (Internal Server Error) AxiosError {message: 'Request failed with status code 500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', ...}
- 服务器日志显示查询超时:
Error during database operation: MongooseError: Operation `places.find()` buffering timed out after 10000ms
- 错误固定发生在
const places = await Place.find({}).limit(10);代码行。
已尝试操作
- 使用async/await确保查询前连接已建立
- 验证MongoDB服务器运行正常,Shell查询可正常返回结果
- 独立测试脚本可成功连接并执行查询
解决方案
1. 绑定模型到已连接的Mongoose实例
问题核心是模型初始化时可能未关联到已连接的Mongoose实例,导致查询无法路由到正确连接。
修改Place.js,改为返回模型生成函数:
import mongoose from 'mongoose'; const placeSchema = new mongoose.Schema({ country: String, population: Number, work: { pub: Number, pub_ratio: Number, languages: { primary: String, secondary: String, }, avg: Number, attendance: Number, }, culture: { food_type: String, climate: String, internet_penetration: String, transportation_infra: String, crime_rate: String, recreation: String, }, }); placeSchema.index({ 'culture.recreation': 1 }); export default function getPlaceModel(mongooseInstance) { return mongooseInstance.models.Place || mongooseInstance.model('Place', placeSchema, 'places'); }
修改match-places.js,使用连接后的实例生成模型,并调整查询条件和返回字段:
import connectDB from '../../../lib/connectDB'; import getPlaceModel from '../../../backend/models/Place'; export default async (req, res) => { if (req.method === 'POST') { try { console.log("Connecting to database..."); const mongoose = await connectDB(); console.log("Connected to database"); // 使用已连接的实例生成模型 const Place = getPlaceModel(mongoose); const userPreferences = req.body; console.log("User Preferences: ", userPreferences); console.log("Starting MongoDB query..."); // 查询匹配culture.recreation = 'soccer'的数据 const places = await Place.find({ 'culture.recreation': 'soccer' }).limit(10); console.log("Places found: ", places); if (places.length === 0) { console.error("No places found."); return res.status(404).json({ error: "No places found." }); } // 返回前端期望的recommendation字段 res.status(200).json({ recommendation: places[0] }); } catch (error) { console.error("Error during database operation: ", error); res.status(500).json({ error: error.message }); } } else { res.status(405).end(); // Method Not Allowed } };
2. 增加连接状态校验
在match-places.js中添加连接状态检查,确保查询前连接已就绪:
const mongoose = await connectDB(); console.log("Connection state:", mongoose.connection.readyState); // readyState=1表示已连接,0=断开,2=连接中,3=断开中
3. 统一前后端字段映射
前端期望接收response.data.recommendation,确保API返回的字段与前端一致,避免前端无法获取数据。
验证步骤
- 重启Next.js开发服务器
- 提交表单,查看服务器日志是否输出查询到的匹配数据
- 前端页面应成功展示
culture.recreation = 'soccer'的地点信息
内容的提问来源于stack exchange,提问作者Kane01
相关产品推荐
相关产品推荐

