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
相关产品推荐
相关产品推荐

