NestboltNestbolt

@nestbolt/audit-log

Configuration

Configure @nestbolt/audit-log module options including actor resolution, disabled actions, excluded fields, and metadata.

The AuditLogModule provides two registration methods: forRoot() for static configuration and forRootAsync() for configuration that depends on other providers (such as ConfigService). The module is registered globally, so you only need to import it once in your root module.

forRoot (Static Configuration)

Use forRoot() when all configuration values are known at compile time:

import { AuditLogModule } from "@nestbolt/audit-log";
import { RequestActorResolver } from "./audit/request-actor.resolver";

@Module({
  imports: [
    AuditLogModule.forRoot({
      defaultActor: { type: "System", id: "system" },
      actorResolver: RequestActorResolver,
      disabledActions: ["deleted"],
      globalExcludedFields: ["password", "passwordHash", "token", "secret", "refreshToken"],
      metadata: { app: "my-app", version: "1.0.0" },
    }),
  ],
})
export class AppModule {}

forRootAsync (Async Configuration)

Use forRootAsync() when configuration values need to be resolved from other providers at runtime:

import { AuditLogModule } from "@nestbolt/audit-log";
import { ConfigModule, ConfigService } from "@nestjs/config";

@Module({
  imports: [
    AuditLogModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        defaultActor: {
          type: "System",
          id: config.get<string>("APP_NAME", "unknown"),
        },
        globalExcludedFields: config
          .get<string>("AUDIT_EXCLUDED_FIELDS", "password,token")
          .split(","),
        metadata: {
          app: config.get<string>("APP_NAME"),
          environment: config.get<string>("NODE_ENV"),
        },
      }),
    }),
  ],
})
export class AppModule {}

The forRootAsync method accepts the following options:

PropertyTypeDescription
importsany[]Modules to import (makes their providers available to useFactory)
injectany[]Providers to inject into useFactory
useFactory(...args: any[]) => AuditLogModuleOptions | Promise<AuditLogModuleOptions>Factory function that returns the module options

When using forRootAsync with an actorResolver, the module automatically instantiates the resolver class using NestJS's ModuleRef.create(), so the resolver can have its own injected dependencies.

Configuration Options Reference

All options are optional. Calling AuditLogModule.forRoot() with no arguments uses the defaults.

defaultActor

defaultActor?: { type: string; id: string }

Sets a fallback actor when no actorResolver is configured, or when the resolver returns null. This is useful for background jobs, seeders, or CLI commands where there is no authenticated user.

AuditLogModule.forRoot({
  defaultActor: { type: "System", id: "system" },
});

With this configuration, all audit log entries that cannot resolve an actor will have actorType: "System" and actorId: "system".

If neither defaultActor nor actorResolver is set, the actor_type and actor_id columns will be null.

actorResolver

actorResolver?: Type<ActorResolver>

A class that implements the ActorResolver interface. The module instantiates this class as a NestJS provider, so it can inject other services (such as a request-scoped context service).

AuditLogModule.forRoot({
  actorResolver: RequestActorResolver,
});

The resolver is called automatically for both subscriber-based (automatic) and manual logging when no explicit actor is provided to AuditLogService.log(). See Actor Resolution for full details.

disabledActions

disabledActions?: AuditAction[]  // "created" | "updated" | "deleted"

Globally disable specific audit actions. When an action is disabled, the subscriber will skip it for all entities, regardless of their individual @Auditable() configuration.

// Only track creates and updates; skip deletes globally
AuditLogModule.forRoot({
  disabledActions: ["deleted"],
});
// Only track updates (skip creates and deletes)
AuditLogModule.forRoot({
  disabledActions: ["created", "deleted"],
});

This option applies only to the automatic subscriber. Manual logging via AuditLogService.log() is not affected by disabledActions.

globalExcludedFields

globalExcludedFields?: string[]

Fields to exclude from audit log diffs across all entities. This is applied in addition to any per-entity except list and the always-excluded fields (id, createdAt, updatedAt, created_at, updated_at).

AuditLogModule.forRoot({
  globalExcludedFields: ["password", "passwordHash", "token", "secret", "refreshToken", "apiKey"],
});

Use this for sensitive fields that should never appear in audit logs, regardless of which entity they belong to.

metadata

metadata?: Record<string, any>

Extra metadata attached to every audit log entry. This is stored in the metadata JSON column and can be used to record application-level context such as the application name, version, or deployment environment.

AuditLogModule.forRoot({
  metadata: {
    app: "billing-service",
    version: "2.3.1",
    environment: "production",
  },
});

When a manual audit log entry provides its own metadata, the manual metadata is used instead of the global metadata (it does not merge).

Full Configuration Example

Here is a complete example combining all options with async configuration:

import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { EventEmitterModule } from "@nestjs/event-emitter";
import { AuditLogModule } from "@nestbolt/audit-log";
import { RequestActorResolver } from "./audit/request-actor.resolver";

@Module({
  imports: [
    ConfigModule.forRoot(),
    EventEmitterModule.forRoot(),
    TypeOrmModule.forRoot({
      type: "postgres",
      host: "localhost",
      port: 5432,
      database: "myapp",
      autoLoadEntities: true,
    }),
    AuditLogModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        defaultActor: { type: "System", id: "system" },
        actorResolver: RequestActorResolver,
        disabledActions:
          config.get<string>("NODE_ENV") === "test" ? ["created", "updated", "deleted"] : [],
        globalExcludedFields: ["password", "passwordHash", "token", "secret"],
        metadata: {
          app: config.get<string>("APP_NAME", "my-app"),
          environment: config.get<string>("NODE_ENV", "development"),
        },
      }),
    }),
  ],
})
export class AppModule {}

In this example, audit logging is completely disabled in the test environment by disabling all three actions.

Configuration Precedence

Field exclusion is applied in layers. A field is excluded from audit diffs if it matches any of the following:

  1. Always excluded -- id, createdAt, updatedAt, created_at, updated_at are always excluded and cannot be tracked.
  2. Global exclusion -- fields listed in globalExcludedFields are excluded from all entities.
  3. Per-entity exclusion -- fields listed in the except option of @Auditable() are excluded from that specific entity.
  4. Per-entity whitelist -- if the only option is set on @Auditable(), only those fields are tracked, and except is still applied on top.

Action filtering also has a precedence order:

  1. Module-level -- if an action is in disabledActions, it is skipped globally.
  2. Entity-level -- if events is set on @Auditable(), only listed actions are tracked for that entity.

Both conditions must pass for an action to be logged.

Next Steps