@allnow/nestjs-audit-log
v0.2.0
Published
Reusable audit log module for NestJS with request context, structured payloads, and pluggable storage.
Downloads
242
Maintainers
Readme
@allnow/nestjs-audit-log
Reusable NestJS audit log module based on the audit concept from bms-core-service.
It provides:
- request context capture: user id, username, IP, user-agent, request id
- structured audit payloads for
CREATE,UPDATE,DELETE, or custom events - pluggable storage so logs can go to TypeORM, a queue, Kafka, RabbitMQ, HTTP, or any custom sink
- optional
@AuditLog()decorator and interceptor for controller-level action logs - TypeORM entity and storage helper for an
audit_logstable
Install
npm install @allnow/nestjs-audit-logPeer dependencies:
npm install @nestjs/common @nestjs/core reflect-metadata rxjsFor the TypeORM helper:
npm install typeorm @nestjs/typeormBasic Usage
import { Module } from '@nestjs/common';
import { AuditLogModule } from '@allnow/nestjs-audit-log';
@Module({
imports: [
AuditLogModule.forRoot(),
],
})
export class AppModule {}By default logs are written through Nest Logger. Production applications should provide a storage adapter.
Custom Storage
import { Injectable } from '@nestjs/common';
import { AuditLogPayload, AuditLogStorage } from '@allnow/nestjs-audit-log';
@Injectable()
export class DatabaseAuditStorage implements AuditLogStorage {
async save(payload: AuditLogPayload): Promise<void> {
// insert into DB, publish to queue, etc.
}
}@Module({
imports: [
AuditLogModule.forRoot({
storage: DatabaseAuditStorage,
failSilently: true,
}),
],
providers: [DatabaseAuditStorage],
})
export class AppModule {}Manual Logging
import { AuditLoggerService } from '@allnow/nestjs-audit-log';
export class FuelTypeService {
constructor(private readonly auditLogger: AuditLoggerService) {}
async update(id: number, dto: UpdateFuelTypeDto) {
const before = await this.repo.findOneByOrFail({ id });
const saved = await this.repo.save({ ...before, ...dto });
await this.auditLogger.logUpdate({
before,
after: saved,
entityType: 'FuelType',
entityId: id,
action: 'updateFuelType',
});
return saved;
}
}Controller Decorator
Register the interceptor once:
import { APP_INTERCEPTOR } from '@nestjs/core';
import { AuditLogInterceptor } from '@allnow/nestjs-audit-log';
@Module({
providers: [
{ provide: APP_INTERCEPTOR, useClass: AuditLogInterceptor },
],
})
export class AppModule {}Use it on handlers:
import { AuditLog } from '@allnow/nestjs-audit-log';
@Post()
@AuditLog({ event: 'CREATE', entityType: 'FuelType', entityId: 'id', newValuesPath: 'data' })
create(@Body() dto: CreateFuelTypeDto) {
return this.service.create(dto);
}entityId: 'id' reads result.id. Dot paths such as data.id are supported. entityType, entityId,
metadata, and newValues also accept callbacks:
@AuditLog({
event: 'CREATE',
entityType: (result) => result.type,
entityId: (result) => result.data.id,
newValuesPath: 'entries.data',
metadata: (_result, req: any) => ({ requestBody: req.body }),
})TypeORM Storage
The package exports AuditLogTypeOrmModule, TypeOrmAuditLogEntity, and TypeOrmAuditLogStorage.
For the common case, let the package wire TypeOrmModule.forFeature() and the storage provider:
import { Module } from '@nestjs/common';
import {
AuditLogTypeOrmModule,
} from '@allnow/nestjs-audit-log';
@Module({
imports: [
AuditLogTypeOrmModule.forRoot({
failSilently: true,
}),
],
})
export class AppModule {}You can also use the base module directly:
AuditLogModule.forRoot({
storage: 'typeorm',
});Recommended indexes are included on the entity:
entity_type,entity_idevent,created_atuser_id,created_atrequest_id
For transactional manual writes, pass the active TypeORM manager:
await this.dataSource.transaction(async (manager) => {
const saved = await manager.save(FuelTypeEntity, next);
await this.auditLogger.logUpdate(
{ before, after: saved, entityType: 'FuelType', entityId: saved.id },
{ manager },
);
});Safety Options
String fields are truncated before save using the entity column limits. JSON payloads are redacted recursively.
Default redacted keys are password, token, authorization, and secret.
AuditLogModule.forRoot({
redactKeys: ['password', 'token', 'authorization', 'secret', 'apiKey'],
truncate: {
userAgent: 512,
},
});Request Context
The middleware reads:
- request id from
x-request-id, or generates a UUID - user from
request.user.id,request.user.userId, orrequest.user.sub - username from
request.user.username,request.user.email, orrequest.user.name - IP and user-agent from the request
Override user resolution:
AuditLogModule.forRoot({
userResolver: {
resolve: (req: any) => ({
id: req.auth?.accountId,
username: req.auth?.displayName,
}),
},
});Publish
Before publishing, confirm name, version, and author in package.json.
npm install
npm run build
npm pack --dry-run
npm publish --access public