Prisma:如何在开发环境中检测待处理数据库迁移?
解决T3 Stack开发环境中Prisma迁移未执行的问题
背景
基于T3 Stack(Next.js + Prisma ORM + TypeScript)开发应用时,拉取同事代码经常会遇到新的数据库迁移文件,但开发环境数据库没同步执行这些迁移,导致应用出现各种莫名异常,要花大量时间排查才发现根源是迁移没跑。
期望实现
- 开发环境启动时,若存在待处理迁移,直接让应用崩溃并给出明确提示(类似Ruby on Rails的默认行为)
- 开发服务器运行时(依赖热重载,开发者常不重启),若有未处理迁移,让所有HTTP请求失败并显示友好提示
疑问解答与实现方案
1. 如何在代码中获取Prisma迁移状态
Prisma Client确实没有直接提供迁移状态的API,但可以通过Node.js的child_process调用Prisma CLI的migrate status命令,解析输出结果来判断是否有待执行迁移。
示例代码:
import { execSync } from 'child_process'; export async function hasPendingMigrations(): Promise<boolean> { try { // 执行prisma migrate status并获取JSON格式输出 const output = execSync('npx prisma migrate status --json', { encoding: 'utf-8' }); const status = JSON.parse(output); // 判断是否存在待执行迁移 return status.pendingMigrations.length > 0; } catch (error) { // 命令执行失败(比如无迁移目录),默认视为无待处理迁移 return false; } }
⚠️ 注意:这个逻辑只在开发环境使用,生产环境不要调用CLI命令,避免性能和安全问题。
2. 在每个HTTP请求中间件中添加检查的影响
- 性能损耗:每次请求都执行
migrate status会增加请求延迟,开发环境虽对性能要求不高,但频繁调用仍有感知。可以优化为缓存检查结果,比如每隔30秒重新检查一次,而非每次请求都触发。 - 热重载兼容性:Next.js热重载会重新执行代码,缓存逻辑要注意避免内存泄漏,也可以监听迁移目录的文件变化,触发重新检查。
- 开发体验:能及时拦截请求并提示迁移,避免无意义的调试,但如果检查逻辑误判,会导致所有请求失败,反而影响开发效率。
3. 其他可选解决方案
- 启动脚本强制检查:在
package.json的dev脚本中前置迁移检查,比如:
在"scripts": { "dev": "node scripts/check-migrations.js && next dev" }check-migrations.js中调用上述hasPendingMigrations方法,若返回true则抛出错误终止进程。 - Git钩子:配置
post-checkout或post-pull钩子,拉取代码后自动检查迁移,提示开发者执行prisma migrate dev。 - 开发环境自动执行迁移:启动时自动执行
prisma migrate dev,但要注意这会直接修改数据库,适合团队共用开发数据库的场景,或配合种子脚本恢复测试数据。 - 自定义Next.js服务器:用自定义服务器替代默认dev服务器,在启动时执行迁移检查,同时定期在运行中重新校验。
具体实现示例
启动时检查(Next.js 13+ App Router)
在app/globals.ts中添加启动校验:
import { hasPendingMigrations } from '@/utils/migrate-check'; if (process.env.NODE_ENV === 'development') { (async () => { const pending = await hasPendingMigrations(); if (pending) { console.error('❌ 存在待执行的数据库迁移,请先运行:npx prisma migrate dev'); process.exit(1); // 终止应用启动 } })(); }
请求中间件检查(Next.js Middleware)
在middleware.ts中添加全局请求拦截:
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { hasPendingMigrations } from '@/utils/migrate-check'; // 缓存检查结果,减少重复执行 let lastCheckedTimestamp = 0; let hasPending = false; export async function middleware(request: NextRequest) { if (process.env.NODE_ENV !== 'development') { return NextResponse.next(); } // 每隔30秒重新检查一次 const now = Date.now(); if (now - lastCheckedTimestamp > 30000) { hasPending = await hasPendingMigrations(); lastCheckedTimestamp = now; } if (hasPending) { return new NextResponse( '⚠️ 请先执行数据库迁移:npx prisma migrate dev', { status: 503, headers: { 'Content-Type': 'text/plain' } } ); } return NextResponse.next(); } // 配置中间件生效范围(排除静态资源) export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], };
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

