如何在Prisma中追踪PostgreSQL数据库的表变更?
PostgreSQL + Prisma 实现变更追踪的方案
一、Prisma原生支持情况
Prisma目前没有内置的变更追踪/审计日志功能,需要通过其他方式实现。
二、可行方案
1. PostgreSQL 原生触发器
利用PostgreSQL的触发器功能,在数据库层面自动捕获变更,不受应用层操作方式限制(包括直接修改数据库的操作)。
实现步骤:
- 创建通用审计表,存储所有变更记录:
CREATE TABLE audit_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), table_name TEXT NOT NULL, record_id TEXT NOT NULL, -- 关联原表记录ID changed_by TEXT NOT NULL, -- 变更人ID/标识 change_reason TEXT, -- 变更原因备注 old_data JSONB, -- 变更前数据 new_data JSONB, -- 变更后数据 change_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, operation_type TEXT NOT NULL CHECK (operation_type IN ('INSERT', 'UPDATE', 'DELETE')) );
- 创建触发器函数,用于插入审计记录:
CREATE OR REPLACE FUNCTION log_changes() RETURNS TRIGGER AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO audit_logs (table_name, record_id, changed_by, change_reason, new_data, operation_type) VALUES (TG_TABLE_NAME, NEW.id::TEXT, current_setting('app.current_user'), current_setting('app.change_reason', true), to_jsonb(NEW), 'INSERT'); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN INSERT INTO audit_logs (table_name, record_id, changed_by, change_reason, old_data, new_data, operation_type) VALUES (TG_TABLE_NAME, OLD.id::TEXT, current_setting('app.current_user'), current_setting('app.change_reason', true), to_jsonb(OLD), to_jsonb(NEW), 'UPDATE'); RETURN NEW; ELSIF TG_OP = 'DELETE' THEN INSERT INTO audit_logs (table_name, record_id, changed_by, change_reason, old_data, operation_type) VALUES (TG_TABLE_NAME, OLD.id::TEXT, current_setting('app.current_user'), current_setting('app.change_reason', true), to_jsonb(OLD), 'DELETE'); RETURN OLD; END IF; END; $$ LANGUAGE plpgsql;
- 给需要追踪的表(如
Task)绑定触发器:
CREATE TRIGGER task_change_trigger AFTER INSERT OR UPDATE OR DELETE ON "Task" FOR EACH ROW EXECUTE FUNCTION log_changes();
- 在Prisma操作前,通过
$queryRaw设置当前用户和变更原因:
await prisma.$queryRaw`SET app.current_user = ${userId}`; await prisma.$queryRaw`SET app.change_reason = ${changeReason}`; // 执行Task的更新/插入/删除操作 await prisma.task.update(...);
优缺点:
- 优点:覆盖所有数据库操作(包括直接SQL修改),无需在应用层重复编写逻辑;通用审计表无需随业务Schema迭代频繁修改。
- 缺点:调试和维护需要SQL知识;变更原因需要通过会话变量传递,应用层需额外处理。
2. Prisma 中间件
通过Prisma Middleware拦截CRUD操作,在应用层自动生成历史记录,适合只需要追踪通过Prisma发起的操作的场景。
实现步骤:
- 定义通用的历史记录模型:
model AuditLog { id String @id @default(uuid()) tableName String recordId String changedBy String changeReason String? oldData Json newData Json? changeTime DateTime @default(now()) operation String @db.VarChar(10) // INSERT/UPDATE/DELETE }
- 编写Prisma中间件,拦截更新/删除/插入操作:
const auditMiddleware = async (params: any, next: any) => { const { model, action, args } = params; // 只处理需要追踪的模型,比如Task const trackedModels = ['Task']; if (!trackedModels.includes(model)) { return next(params); } // 获取上下文传递的用户ID和变更原因 const { userId, changeReason } = params.context || {}; if (!userId) { throw new Error('变更人ID不能为空'); } let oldData = null; let newData = null; if (action === 'update' || action === 'delete') { // 查询旧数据 oldData = await prisma[model].findUnique({ where: args.where, include: { status: true }, // 按需关联子模型 }); } // 执行原操作 const result = await next(params); if (action === 'create' || action === 'update') { newData = result; } // 插入审计记录 await prisma.auditLog.create({ data: { tableName: model, recordId: args.where?.id || result.id, changedBy: userId, changeReason, oldData: oldData ? JSON.parse(JSON.stringify(oldData)) : null, newData: newData ? JSON.parse(JSON.stringify(newData)) : null, operation: action.toUpperCase(), }, }); return result; }; // 扩展Prisma客户端 const prisma = new PrismaClient().$extends({ query: auditMiddleware, });
- 调用Prisma方法时传递上下文:
await prisma.task.update({ where: { id: 'task-id' }, data: { statusId: 2 }, context: { userId: 'user-123', changeReason: '任务完成' }, });
优缺点:
- 优点:和Prisma深度集成,用TypeScript编写,符合应用层开发习惯;可以灵活结合业务逻辑处理审计规则。
- 缺点:只能捕获通过Prisma发起的操作,直接修改数据库的操作无法追踪;需要维护中间件逻辑适配Schema变更。
3. 社区插件
Prisma社区有现成的审计日志插件(如prisma-audit-log),可开箱实现变更追踪,减少自定义代码量。这类插件通常支持自动生成历史记录、关联变更人、存储变更前后数据等功能,只需按配置文档快速集成即可。
三、方案选择建议
- 若需追踪所有数据库操作(包括直接SQL修改):优先选择PostgreSQL触发器方案。
- 若只需追踪应用层通过Prisma发起的操作,且希望灵活控制:选择Prisma中间件方案。
- 若追求快速实现,不想自定义太多代码:选择社区审计插件。
内容的提问来源于stack exchange,提问作者caseyf
相关产品推荐
相关产品推荐

