NextJS middleware.js调用MongoDB报错:connect不是函数
问题描述
使用Next.js 14.0.4 + MongoDB + Mongoose 8.0.3搭建应用,从页面组件调用数据库操作函数(如updateUser)时一切正常,但在Clerk的middleware.js中调用fetchUser时,触发错误:
mongoose__WEBPACK_IMPORTED_MODULE_0___default(...).connect is not a function
相关核心代码片段:
数据库模型文件 /lib/models/user.model.js
"use server"; import mongoose, { Schema } from "mongoose"; mongoose.connect(process.env.MONGODB_URI); mongoose.Promise = global.Promise; const userSchema = new Schema( { // 字段定义 }, { timestamps: true } ); const User = mongoose.models.User || mongoose.model("User", userSchema); export default User;
数据库操作函数 /lib/actions/actions.js
"use server"; import User from "../models/user.model"; export async function updateUser(params) { try { await User.findOneAndUpdate( { /* 查询条件 */ }, { upsert: true } ); } catch (error) { throw new Error(`Faild to create/update user ${error.message}`); } } export async function fetchUser(ClerkUserId) { try { const user = await User.findOne({ ClerkUserId }); return user; } catch (error) { console.error("Error fetching user:", error); return null; } }
调用出错的 middleware.js
"use server"; import { authMiddleware, redirectToSignIn } from "@clerk/nextjs"; import { fetchUser } from "./lib/actions/actions"; export default authMiddleware({ publicRoutes: ["/"], afterAuth: async (auth, req, evt) => { if (!auth.userId && !auth.isPublicRoute) { return redirectToSignIn({ returnBackUrl: req.url }); } else if (auth.userId) { await fetchUser(auth.userId); } }, }); export const config = { matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], };
原因分析
核心问题在于Next.js Middleware运行在Edge Runtime环境,而Mongoose 8.x不支持该环境:
- Edge Runtime限制了Node.js核心API(如
net、fs)的使用,而Mongoose的连接逻辑依赖这些Node.js专属功能,导致在Edge环境中无法正常初始化。 - 在Edge环境下,Mongoose导出的对象不包含
connect方法,直接触发了报错。 - 页面组件调用的Server Actions运行在Node.js Runtime,因此可以正常使用Mongoose;但Clerk的
authMiddleware强制使用Edge Runtime,无法切换到Node.js环境。
解决方案
方案1:将数据库操作移到API Route,在Middleware中调用API
- 创建API路由(如
app/api/fetch-user/route.js)处理用户查询:
import { NextResponse } from "next/server"; import User from "@/lib/models/user.model"; export async function GET(request) { const { searchParams } = new URL(request.url); const clerkUserId = searchParams.get("clerkUserId"); if (!clerkUserId) { return NextResponse.json({ error: "Missing clerkUserId" }, { status: 400 }); } try { const user = await User.findOne({ ClerkUserId: clerkUserId }); return NextResponse.json(user); } catch (error) { console.error("Error fetching user:", error); return NextResponse.json({ error: "Failed to fetch user" }, { status: 500 }); } }
- 修改
middleware.js,通过fetch调用该API:
import { authMiddleware, redirectToSignIn } from "@clerk/nextjs"; export default authMiddleware({ publicRoutes: ["/"], afterAuth: async (auth, req, evt) => { if (!auth.userId && !auth.isPublicRoute) { return redirectToSignIn({ returnBackUrl: req.url }); } else if (auth.userId) { await fetch(`${process.env.NEXT_PUBLIC_APP_URL}/api/fetch-user?clerkUserId=${auth.userId}`); } }, }); export const config = { matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], };
方案2:将用户检查逻辑移到Server Component
如果Middleware不需要立即使用用户数据,可将查询逻辑移到页面的Server Component中:
- 移除
middleware.js中的fetchUser调用。 - 在页面组件(如
app/dashboard/page.js)中查询用户:
import { fetchUser } from "@/lib/actions/actions"; import { auth } from "@clerk/nextjs"; export default async function DashboardPage() { const { userId } = auth(); const user = await fetchUser(userId); return <div>Welcome {user?.username}</div>; }
方案3:改用支持Edge的MongoDB驱动
若必须在Middleware中直接操作数据库,可放弃Mongoose,使用官方mongodb包(支持Edge Runtime):
- 安装依赖:
npm install mongodb
- 在Middleware中直接使用MongoDB驱动:
import { MongoClient } from "mongodb"; import { authMiddleware, redirectToSignIn } from "@clerk/nextjs"; const client = new MongoClient(process.env.MONGODB_URI); export default authMiddleware({ publicRoutes: ["/"], afterAuth: async (auth, req, evt) => { if (!auth.userId && !auth.isPublicRoute) { return redirectToSignIn({ returnBackUrl: req.url }); } else if (auth.userId) { await client.connect(); const db = client.db(); const user = await db.collection("users").findOne({ ClerkUserId: auth.userId }); // 处理用户数据 await client.close(); } }, }); export const config = { matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], };
内容的提问来源于stack exchange,提问作者roee
相关产品推荐
相关产品推荐

