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

Next.js中间件中Prisma连接失败,如何解决该问题?

问题:Next.js中间件中使用Prisma触发环境不兼容错误

错误信息

Error [PrismaClientValidationError]: PrismaClient failed to initialize because it wasn't configured to run in this environment (Vercel Edge Functions or Edge Middleware).
In order to run Prisma Client in an edge runtime, you will need to configure one of the following options:
- Enable Driver Adapters: https://pris.ly/d/driver-adapters
- Enable Accelerate: https://pris.ly/d/accelerate

版本信息

  • Next.js: 14.2.4
  • @prisma/client: ^5.15.0
  • prisma: ^5.15.0
  • 数据库:SQLite

项目结构

project-root/
│
├── /prisma
│   ├── schema.prisma
│   └──...
│
├── /src
│   ├── /app
│   │    ├── /(userFacing) 
│   │    │    └── ...
│   │    ├── layout.tsx
│   │    └── providers.tsx
│   ├── /components 
│   │    └── ...
│   ├── /lib 
│   │    └── isValidPassword.ts
│   ├── /db 
│   │    └── db.ts
│   └── middleware.ts
│
├── .env
├── .eslintrc.json
├── .gitignore
├── components.json
├── next-env.d.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── tailwind.config.ts
├── tsconfig.json

相关代码

middleware.ts

import { NextRequest, NextResponse } from "next/server";
import isValidPassword from "./lib/isValidPassword";

export default async function middleware(req: NextRequest) {
    if ((await isAuthenticated(req)) === false) {
        return new NextResponse("Unauthorized", {
            status: 401,
            headers: { "WWW-Authenticate": "Basic" },
        })
    }
}

async function isAuthenticated(req: NextRequest) {
    const authHeader = req.headers.get("Authorization") || req.headers.get("authorization");

    if (!authHeader || !authHeader.startsWith("Basic ")) {
        return false;
    }

    const base64Credentials = authHeader.split(" ")[1];
    const credentials = Buffer.from(base64Credentials, "base64").toString("utf-8");
    const [username, password] = credentials.split(":");

    const isValid = await isValidPassword(username, password);
    return isValid;
}


export const config = {
    matcher: "/"
};

isValidPassword.ts

import bcrypt from "bcrypt";
import db from "../db/db"

export default async function isValidPassword(username: string, password: string) {

    try {

        const user = await db.user.findUnique({
            where: {
                username: username,
            },
        });

        if (!user) {
            console.log(`User '${username}' not found.`);
            return false;
        }

        const hashedPassword = await bcrypt.hash(password, 10)
        const isMatch = await bcrypt.compare(hashedPassword, user.password)

        return isMatch

    } catch (error) {
        console.error(`Error validating passwword ${error}`);
        return false;
    }
}

db.ts

import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => {
    return new PrismaClient()
}

declare global {
    var prisma: PrismaClient | undefined;
}

const db = global.prisma || prismaClientSingleton();

export default db

if (process.env.NODE_ENV !== 'production') {
    global.prisma = db;
}

schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id       Int     @id @default(autoincrement())
  username String  @unique
  password String
  admin    Boolean @default(false)
}

问题分析与解决方案

错误原因

  1. Runtime环境不兼容:Next.js的Middleware默认运行在Edge Runtime,而Prisma Client是为Node.js runtime设计的,不支持Edge环境。尤其是SQLite作为文件型数据库,Edge环境没有文件系统访问权限,完全无法适配。
  2. 密码验证逻辑错误:你在isValidPassword中先对用户输入的密码重新哈希,再和数据库中存储的哈希密码对比,这是错误的——bcrypt.compare应该直接接收原始密码和数据库中的哈希值,不需要提前哈希输入值。

解决步骤

1. 重构认证逻辑,避免在Middleware中使用Prisma

Middleware不能直接操作数据库,需要把认证逻辑移到Node.js runtime环境中(比如API Route或Server Component):

  • 创建API路由src/app/api/auth/route.ts,处理Basic Auth验证:
import { NextRequest, NextResponse } from "next/server";
import bcrypt from "bcrypt";
import db from "@/db/db";

export async function POST(req: NextRequest) {
  const authHeader = req.headers.get("Authorization");
  if (!authHeader || !authHeader.startsWith("Basic ")) {
    return NextResponse.json({ valid: false }, { status: 401 });
  }

  const base64Credentials = authHeader.split(" ")[1];
  const credentials = Buffer.from(base64Credentials, "base64").toString("utf-8");
  const [username, password] = credentials.split(":");

  try {
    const user = await db.user.findUnique({ where: { username } });
    if (!user) {
      return NextResponse.json({ valid: false }, { status: 401 });
    }

    // 修复密码对比逻辑
    const isMatch = await bcrypt.compare(password, user.password);
    return NextResponse.json({ valid: isMatch }, { status: isMatch ? 200 : 401 });
  } catch (error) {
    console.error("Auth error:", error);
    return NextResponse.json({ valid: false }, { status: 500 });
  }
}
  • 修改Middleware,调用这个API进行验证:
import { NextRequest, NextResponse } from "next/server";

export default async function middleware(req: NextRequest) {
  const authHeader = req.headers.get("Authorization");
  if (!authHeader || !authHeader.startsWith("Basic ")) {
    return new NextResponse("Unauthorized", {
      status: 401,
      headers: { "WWW-Authenticate": "Basic" },
    });
  }

  // 调用API验证身份
  const authRes = await fetch(new URL("/api/auth", req.url), {
    method: "POST",
    headers: { Authorization: authHeader },
  });

  if (!authRes.ok) {
    return new NextResponse("Unauthorized", {
      status: 401,
      headers: { "WWW-Authenticate": "Basic" },
    });
  }

  return NextResponse.next();
}

export const config = {
  matcher: "/"
};

2. 修复密码验证逻辑

删除isValidPassword中多余的哈希步骤,正确代码如下(如果保留这个函数的话):

import bcrypt from "bcrypt";
import db from "../db/db"

export default async function isValidPassword(username: string, password: string) {
    try {
        const user = await db.user.findUnique({
            where: { username },
        });

        if (!user) {
            console.log(`User '${username}' not found.`);
            return false;
        }

        // 直接用原始密码对比数据库中的哈希值
        const isMatch = await bcrypt.compare(password, user.password);
        return isMatch;

    } catch (error) {
        console.error(`Error validating password: ${error}`);
        return false;
    }
}

3. 关键说明

  • 由于你使用SQLite,无法通过Driver Adapters或Accelerate适配Edge环境,必须将数据库操作放在Node.js runtime中。
  • Middleware的核心作用是路由拦截和重定向,复杂的业务逻辑(如数据库操作)应该交给API Route或Server Component处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 09:10:58