Run the TypeORM migration to create the audit_logs table:
# Generate migration (if using TypeORM CLI)
npm run typeorm migration:generate -- src/migrations/AddAuditLogs
# Or run the pre-created migration
npm run typeorm migration:runIf migrations are auto-run on startup (synchronize: true), the table will be created automatically.
For each service that needs audit logging, inject the AuditTrailService:
import { AuditTrailService } from '../audit/services/audit-trail.service';
import { AuditActionType, AuditEntityType } from '../audit/entities/audit-log.entity';
@Injectable()
export class YourService {
constructor(
private auditTrailService: AuditTrailService,
// ... other dependencies
) {}
async yourMethod() {
// Your implementation
await this.auditTrailService.log({
actionType: AuditActionType.CLAIM_CREATED,
entityType: AuditEntityType.CLAIM,
entityId: 'entity-id',
userId: 'user-id',
description: 'Action description',
afterState: { /* entity state */ },
});
}
}For automatic logging with the decorator, just add it to your methods:
import { AuditLog } from '../audit/decorators/audit-log.decorator';
import { AuditActionType, AuditEntityType } from '../audit/entities/audit-log.entity';
@AuditLog({
actionType: AuditActionType.CLAIM_CREATED,
entityType: AuditEntityType.CLAIM,
descriptionTemplate: 'Claim created',
captureAfterState: true,
})
async createClaim(data: any): Promise<Claim> {
// Method implementation
}Test that audit logging is working:
# Start your application
npm run start
# Make a request that triggers an audited action
curl -X POST http://localhost:3000/claims \
-H "Content-Type: application/json" \
-d '{"data": "..."}'
# Query the audit logs
curl http://localhost:3000/auditasync create(dto: CreateClaimDto, userId: string): Promise<Claim> {
const entity = this.repo.create(dto);
const saved = await this.repo.save(entity);
await this.auditTrailService.log({
actionType: AuditActionType.CLAIM_CREATED,
entityType: AuditEntityType.CLAIM,
entityId: saved.id,
userId,
description: `Created claim: ${saved.id}`,
afterState: this.serializeEntity(saved),
});
return saved;
}async update(id: string, dto: UpdateClaimDto, userId: string): Promise<Claim> {
const before = await this.repo.findOne(id);
const beforeState = this.serializeEntity(before);
Object.assign(before, dto);
const after = await this.repo.save(before);
await this.auditTrailService.log({
actionType: AuditActionType.CLAIM_UPDATED,
entityType: AuditEntityType.CLAIM,
entityId: id,
userId,
description: 'Claim updated',
beforeState,
afterState: this.serializeEntity(after),
});
return after;
}async completeVerification(
claimId: string,
verdict: boolean,
userId: string,
): Promise<void> {
const claim = await this.claimRepo.findOne(claimId);
// Perform verification
claim.verified = true;
claim.verdict = verdict;
await this.claimRepo.save(claim);
// Log with rich metadata
await this.auditTrailService.log({
actionType: AuditActionType.VERIFICATION_COMPLETED,
entityType: AuditEntityType.CLAIM,
entityId: claimId,
userId,
description: `Claim ${verdict ? 'verified' : 'rejected'}`,
beforeState: { verified: false },
afterState: { verified: true, verdict },
metadata: {
verificationTime: Date.now(),
verificationMethod: 'automated',
confidence: 0.95,
sources: ['source1', 'source2'],
},
});
}# Get all claims related actions
curl "http://localhost:3000/audit?entityType=CLAIM"
# Get user's actions
curl "http://localhost:3000/audit/user/user-id-123"
# Get claim resolution history
curl "http://localhost:3000/audit/entity/CLAIM/claim-id-123"
# Get change history
curl "http://localhost:3000/audit/changes/CLAIM/claim-id-123"
# Get summary for last 30 days
curl "http://localhost:3000/audit/summary?entityType=CLAIM&days=30"import { HttpClient } from '@angular/common/http';
export class AuditService {
constructor(private http: HttpClient) {}
getEntityLogs(entityType: string, entityId: string) {
return this.http.get(`/audit/entity/${entityType}/${entityId}`);
}
getUserLogs(userId: string) {
return this.http.get(`/audit/user/${userId}`);
}
getChangeHistory(entityType: string, entityId: string) {
return this.http.get(`/audit/changes/${entityType}/${entityId}`);
}
getSummary(entityType?: string, days?: number) {
let url = '/audit/summary';
const params = [];
if (entityType) params.push(`entityType=${entityType}`);
if (days) params.push(`days=${days}`);
if (params.length) url += '?' + params.join('&');
return this.http.get(url);
}
}- Indexing - The migration creates indexes on frequently queried fields
- Pagination - Always use limit/offset for large datasets
- Async Logging - Audit logging is non-blocking and won't slow requests
- Retention - Consider archiving old logs periodically
- Check that AuditModule is imported in AppModule
- Verify AuditLoggingInterceptor is registered globally
- Confirm database table was created:
SELECT * FROM audit_logs LIMIT 1 - Check logs for errors: Look for "Failed to log audit" messages
- Verify the entityId, userId, or entityType values match
- Check the format: IDs should be exact matches
- Try querying all logs first:
GET /audit - Check database directly for table content
- Add indexes to frequently filtered columns
- Use pagination with smaller page sizes
- Archive old logs regularly with
deleteOldLogs(daysToKeep) - Consider a separate audit database for high-volume systems
Here's a complete example integrating audit logging into claim submission and verification:
// claims.controller.ts
@Post()
async createClaim(@Body() dto: CreateClaimDto, @Request() req) {
return this.claimsService.createClaim(dto, req.user.id);
}
@Post(':id/resolve')
async resolveClaim(
@Param('id') claimId: string,
@Body() dto: ResolveClaimDto,
@Request() req,
) {
return this.claimsService.resolveClaim(
claimId,
dto.verdict,
dto.confidence,
req.user.id,
);
}
// claims.service.ts
@AuditLog({
actionType: AuditActionType.CLAIM_CREATED,
entityType: AuditEntityType.CLAIM,
captureAfterState: true,
})
async createClaim(dto: CreateClaimDto, userId: string): Promise<Claim> {
const claim = await this.claimRepo.save(this.claimRepo.create(dto));
return claim;
}
async resolveClaim(
claimId: string,
verdict: boolean,
confidence: number,
userId: string,
): Promise<Claim> {
const before = await this.claimRepo.findOne(claimId);
before.resolvedVerdict = verdict;
before.confidenceScore = confidence;
const after = await this.claimRepo.save(before);
await this.auditTrailService.log({
actionType: AuditActionType.CLAIM_RESOLVED,
entityType: AuditEntityType.CLAIM,
entityId: claimId,
userId,
description: `Resolved: ${verdict}, confidence: ${confidence}`,
beforeState: { verdict: before.resolvedVerdict, confidence: before.confidenceScore },
afterState: { verdict, confidence },
});
return after;
}
// Query the audit trail
const logs = await this.auditTrailService.getEntityAuditLogs(
AuditEntityType.CLAIM,
claimId,
);- Add audit logging to more services - Extend to user management, rewards, etc.
- Create audit reports - Build compliance and analytics tools
- Set up monitoring - Alert on suspicious patterns
- Implement retention policies - Archive old logs periodically
- Create dashboards - Visualize audit activity over time