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

Next.js客户端组件调用Mongoose报错:connect不是函数求解决方案

问题描述

在部署于Heroku的Next.js项目中,客户端组件直接调用数据库操作代码时出现以下错误:

TypeError: mongoose__WEBPACK_IMPORTED_MODULE_0___default().connect is not a function

相关代码如下:

DB_ChanList.tsx(客户端组件)

'use client';

import Link from 'next/link';
import channelsPool from "@/lib/channels";
import React from 'react';
import {useState,useEffect} from 'react';


export default function DB_ChanList() {
  const [listDisplay,setListDisplay] = useState(true);
  const theChannels = channelsPool()


  const swapDisplay = (e: React.MouseEvent<HTMLButtonElement>) => {
    e.preventDefault()
    setListDisplay(!listDisplay)
  } /* End of swapDisplay */

  return <div>
        {listDisplay &&
      Object.values(theChannels).map(item => (
                <div key={item as string}>
          <button onClick={(e)=>swapDisplay(e)} value={item}>
            <b>{item}</b>
          </button>
                </div>
            ))
        }
        {!listDisplay &&
      <div>THE:unitID::</div>
        }
    </div>
} /* End of DB_ChanList */

channels.tsx

import mongoose, {Schema,Model,models} from "mongoose"
import connectMDB from "./mongoDB"


export default async function channelsPool() {
    const speakSchema = new Schema({
        channelID: {
            type: String,
            required: true
        },
        voiceRecordID: {
            type: String,
            required: true
        },
        timeStamp: {
            type: Number,
            required: true
        }
    })

    interface SpeakDoc extends Document {
        channelID: string
        voiceRecordID: string
        timeStamp: number
    }

    interface SpeakModel extends Model<SpeakDoc> {}

    const theModel = (models && models.Voice) ? (models.Voice as SpeakModel) :
                                            mongoose.model<SpeakDoc, SpeakModel>
                                                    ('Voice',speakSchema,'vxads')

    await connectMDB()

    try {
        console.log('MDB_URI_AUDIO(T)=',process.env.MDB_URI_AUDIO)
        const theResult = await theModel
        .find({},'channelID').distinct('channelID')
  
        return theResult
    } catch (error) {
        console.log('MDB_URI_AUDIO(E)=',process.env.MDB_URI_AUDIO)
        console.error("Error in channelsPool:", error);
        throw error; // Rethrow the error to propagate it.
    }
} /* End of channelsPool */

mongoDB.tsx

import mongoose from "mongoose"

const uri = process.env.MDB_URI_AUDIO!,
            connectMDB = async () => mongoose.connect(uri)

export default connectMDB

已知问题根源:mongoose的数据库连接代码仅能在服务器组件中运行,无法在客户端组件中直接调用。需要实现客户端组件安全访问数据库的方案。


解决方案

客户端组件绝对不能直接连接数据库,这会暴露数据库凭证,带来严重安全风险。正确的做法是通过Next.js的API路由作为中间层,客户端调用API路由,由API路由在服务器端执行数据库操作。

步骤1:创建API路由

在app/api/channels/route.ts(若使用Pages Router则创建pages/api/channels.ts)中创建服务器端API:

import mongoose, { Schema, Model, models } from "mongoose";
import { NextResponse } from "next/server";

const connectMDB = async () => {
  // 避免重复连接
  if (mongoose.connection.readyState === 1) {
    return mongoose.connection.asPromise();
  }
  return mongoose.connect(process.env.MDB_URI_AUDIO!);
};

const speakSchema = new Schema({
  channelID: { type: String, required: true },
  voiceRecordID: { type: String, required: true },
  timeStamp: { type: Number, required: true },
});

interface SpeakDoc extends Document {
  channelID: string;
  voiceRecordID: string;
  timeStamp: number;
}

interface SpeakModel extends Model<SpeakDoc> {}

const VoiceModel = (models && models.Voice) ? 
  (models.Voice as SpeakModel) : 
  mongoose.model<SpeakDoc, SpeakModel>('Voice', speakSchema, 'vxads');

export async function GET() {
  try {
    await connectMDB();
    const channels = await VoiceModel.find({}, 'channelID').distinct('channelID');
    return NextResponse.json(channels);
  } catch (error) {
    console.error("Error fetching channels:", error);
    return NextResponse.json({ error: "Failed to fetch channels" }, { status: 500 });
  }
}

步骤2:修改客户端组件,通过API获取数据

更新DB_ChanList.tsx,使用useEffect和fetch调用API路由获取数据:

'use client';

import React, { useState, useEffect } from 'react';

export default function DB_ChanList() {
  const [listDisplay, setListDisplay] = useState(true);
  const [channels, setChannels] = useState<string[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchChannels = async () => {
      try {
        const res = await fetch('/api/channels');
        if (!res.ok) throw new Error('Failed to fetch channels');
        const data = await res.json();
        setChannels(data);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    };

    fetchChannels();
  }, []);

  const swapDisplay = (e: React.MouseEvent<HTMLButtonElement>) => {
    e.preventDefault();
    setListDisplay(!listDisplay);
  };

  if (loading) return <div>Loading channels...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      {listDisplay && channels.map(item => (
        <div key={item}>
          <button onClick={swapDisplay} value={item}>
            <b>{item}</b>
          </button>
        </div>
      ))}
      {!listDisplay && <div>THE:unitID::</div>}
    </div>
  );
}

步骤3:清理旧代码

删除channels.tsx和mongoDB.tsx中不再需要的客户端可访问数据库逻辑(若其他服务器端代码仍需使用,可保留但确保仅在服务器环境调用)。

关键说明

  • API路由在服务器端运行,安全访问数据库,不会将凭证暴露给客户端。
  • 客户端通过HTTP请求与API交互,符合前后端分离的安全实践。
  • 新增加载状态和错误处理,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 01:56:09