You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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返回的字段与前端一致,避免前端无法获取数据。

验证步骤

  1. 重启Next.js开发服务器
  2. 提交表单,查看服务器日志是否输出查询到的匹配数据
  3. 前端页面应成功展示culture.recreation = 'soccer'的地点信息

内容的提问来源于stack exchange,提问作者Kane01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:05:57