NextJS14 Middleware调用serverless-mysql时出现undefined错误
错误根源
报错TypeError: Cannot read properties of undefined (reading 'slice')出现在serverless-mysql的connect方法中,核心原因有三点:
serverless-mysql不兼容Edge Runtime
Next.js Middleware默认运行在Edge Runtime环境,而serverless-mysql依赖Node.js专属API(如net、tls模块),这些在Edge环境中不可用,导致初始化连接时内部变量未定义,触发slice调用错误。Cookie获取方式错误
在Middleware中,不能直接调用全局cookies()函数,必须从传入的NextRequest对象中获取Cookie,否则会导致Cookie读取失败,后续数据库查询的sessionId异常,间接引发连接逻辑错误。MySQL实例初始化缺失
代码中直接使用mysql.connect(),但未展示mysql实例的初始化代码。若未正确配置数据库连接参数(如host、user、password等),serverless-mysql内部状态未正确初始化,也会触发此类错误。
修复步骤
1. 替换为Edge兼容的数据库客户端
由于serverless-mysql不支持Edge Runtime,建议改用兼容Edge的MySQL客户端,比如@planetscale/database(支持Edge环境):
// src/lib/db.ts import { connect } from '@planetscale/database' const config = { host: process.env.DATABASE_HOST, username: process.env.DATABASE_USERNAME, password: process.env.DATABASE_PASSWORD, } const conn = connect(config) export async function isSessionValid(sessionId: string) { const results = await conn.execute('SELECT * FROM sessions WHERE id = ?', [sessionId]) return results.rows.length > 0 } export async function getUserBySessionId(sessionId: string) { const results = await conn.execute(` SELECT users.* FROM users JOIN sessions ON users.id = sessions.user_id WHERE sessions.id = ? `, [sessionId]) if (results.rows.length === 0) { return null } return new User(results.rows[0].name, results.rows[0].role) }
2. 修正Middleware中Cookie的获取逻辑
必须通过NextRequest对象传递并获取Cookie,同时确保Middleware为异步函数:
修改middleware.ts:
import { NextRequest, NextResponse } from "next/server"; import { isAuthenticated } from "@/lib/auth"; export async function middleware(request: NextRequest) { const authenticated = await isAuthenticated(request); if (!authenticated) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next(); }
修改auth.ts:
import { NextRequest } from "next/server"; import { isSessionValid, getUserBySessionId } from "@/lib/db"; export async function isAuthenticated(request: NextRequest) { const sessionId = request.cookies.get("sessionId")?.value; if (!sessionId) { return false; } if (!await isSessionValid(sessionId)) { return false; } return await getUserBySessionId(sessionId) !== null; }
3. 若坚持使用serverless-mysql,强制使用Node.js Runtime
如果必须保留serverless-mysql,需让Middleware运行在Node.js环境,并正确初始化实例:
步骤1:配置Middleware使用Node.js Runtime
// middleware.ts export const config = { runtime: 'nodejs', // 强制切换到Node.js Runtime }; import { NextRequest, NextResponse } from "next/server"; import { isAuthenticated } from "@/lib/auth"; export async function middleware(request: NextRequest) { const authenticated = await isAuthenticated(request); if (!authenticated) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next(); }
步骤2:正确初始化serverless-mysql实例
// src/lib/db.ts import mysql from 'serverless-mysql' const db = mysql({ config: { host: process.env.MYSQL_HOST, database: process.env.MYSQL_DATABASE, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, }, }) export async function isSessionValid(sessionId: string) { await db.connect(); let results; try { results = await db.query(`SELECT * FROM sessions WHERE id = ?`, [sessionId]); } catch (e: any) { throw new Error("Error executing query: " + e.message); } finally { await db.end(); } return results && results.length > 0; } // getUserBySessionId函数同理,使用db实例而非全局mysql
关键注意事项
- Edge Runtime下避免使用依赖Node.js核心API的库,优先选择Edge兼容的工具。
- Middleware中必须通过
NextRequest对象获取请求相关数据,禁止使用全局函数。 - 异步逻辑必须正确使用
await,原代码中未等待isAuthenticated()会导致逻辑失效。
内容的提问来源于stack exchange,提问作者user23465324

