Skip to content

Audit log ​

The library does not own a table. You pass a writer. After a successful create, update, delete, or custom action, it calls audit.write with a safe event.

ts
createAdmin({
  adapter: prismaAdapter({ prisma }),
  auth: { getCurrentUser },
  audit: {
    write: async (event) => {
      await prisma.adminAuditLog.create({
        data: {
          eventType: event.type,
          modelName: event.modelName,
          recordIds: event.recordIds.map(String),
          actorId: event.actor.id,
          actorEmail: event.actor.email,
          actorRole: event.actor.role,
          metadata: event.metadata,
          createdAt: event.timestamp,
        },
      });
    },
  },
});

That writer is Prisma. With TypeORM, save through your own repository instead of prisma.adminAuditLog.create. You still own the table.

Event shape ​

ts
{
  type: "create" | "update" | "delete" | "action";
  modelName: string;
  recordIds: Array<string | number>;
  actor: { id: string; email: string; role: string };
  timestamp: Date;
  metadata?: Record<string, string | number | boolean | null>;
}

metadata is things like { action: "publish_selected" }. It is never old/new field values, passwords, or tokens.

Failure ​

If write throws, the API reports a failure. The mutation may already have happened — do not treat a failed audit as “the Post was not published.” Make the writer reliable (same database transaction if you need that coupling).

Do not register the log as an admin model ​

The example defines AdminAuditLog and never calls admin.register("AdminAuditLog"). Operators cannot edit history from the sidebar. That is a product choice, not a limitation. If you register it, it becomes ordinary CRUD unless you lock it down with permissions and fields: { *: readOnly }.

Omit audit entirely if you do not want events. Nothing is written by default.

Released under the MIT License.