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

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不支持该环境:

  1. Edge Runtime限制了Node.js核心API(如net、fs)的使用,而Mongoose的连接逻辑依赖这些Node.js专属功能,导致在Edge环境中无法正常初始化。
  2. 在Edge环境下,Mongoose导出的对象不包含connect方法,直接触发了报错。
  3. 页面组件调用的Server Actions运行在Node.js Runtime,因此可以正常使用Mongoose;但Clerk的authMiddleware强制使用Edge Runtime,无法切换到Node.js环境。

解决方案

方案1:将数据库操作移到API Route,在Middleware中调用API

  1. 创建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 });
  }
}
  1. 修改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中:

  1. 移除middleware.js中的fetchUser调用。
  2. 在页面组件(如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):

  1. 安装依赖:
npm install mongodb
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 07:15:57