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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 23:35:36