npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cleancode-id/nestjs-sequelize-auditor

v4.1.0

Published

Audit trail package for NestJS + Sequelize ORM with AsyncLocalStorage context management

Downloads

26

Readme

@cleancode-id/nestjs-sequelize-auditor

🔍 Simple audit trails for NestJS + Sequelize with zero configuration and automatic relationship tracking.

npm version npm_downloads License: MIT

✨ Features

  • 🎯 Zero Setup - Auto-creates audit table and relationships
  • 🔄 Complete Tracking - CREATE, UPDATE, DELETE, RESTORE + bulk operations
  • 🧵 Smart Context - Captures user, IP, URL from HTTP requests automatically
  • 👤 Built-in Creator - include: ["creator"] works out of the box
  • 🎭 Multi-Actor Support - Track different user types (User, Admin, System)
  • 🛡️ Data Security - Exclude/mask sensitive fields, filter creator data globally
  • 🗄️ Multi-DB Support - PostgreSQL, MySQL with proper indexing
  • 📦 TypeScript Native - Full type safety, zero runtime dependencies
  • 🚀 Production Ready - Battle-tested with comprehensive test suite

🚀 Quick Start

Installation

npm install @cleancode-id/nestjs-sequelize-auditor

Module Setup

// app.module.ts
import { AuditModule } from '@cleancode-id/nestjs-sequelize-auditor';

@Module({
  imports: [
    SequelizeModule.forRoot(/* your db config */),
    AuditModule.forRoot({
      autoSync: true,                  // Auto-create audit table
      actorTypes: ['User', 'Admin'],   // Which models can be actors
      creatorFields: ['id', 'name'],   // Global: only return safe fields
      auth: {
        type: 'passport',              // Use Passport.js
        userProperty: 'user',          // req.user
        userIdField: 'id',             // req.user.id
      },
    }),
  ],
})
export class AppModule {}

Model Setup

// user.model.ts
import { Auditable, AuditEvent } from '@cleancode-id/nestjs-sequelize-auditor';

@Auditable({
  exclude: ['password', 'createdAt', 'updatedAt'],
  auditEvents: [AuditEvent.CREATED, AuditEvent.UPDATED, AuditEvent.DELETED],
})
@Table({ tableName: 'users' })
export class User extends Model {
  @Column({ primaryKey: true, autoIncrement: true })
  id: number;

  @Column
  name: string;

  @Column
  email: string;
  
  // ✨ Automatically available:
  // - audits: Audit[] relationship  
  // - creator: User virtual field (filtered by creatorFields)
  // - creationAudit: Audit relationship
}

Usage Examples

@Injectable()
export class UserService {
  constructor(@InjectModel(User) private userModel: typeof User) {}

  // Get user with creator info (only id + name, no password)
  async findWithCreator(id: number) {
    return this.userModel.findByPk(id, {
      include: ['creator']  // ✨ Automatic, secure creator data
    });
  }

  // Pagination with creator
  async getPaginated(page: number, limit: number) {
    return this.userModel.findAndCountAll({
      include: ['creator'],
      limit,
      offset: page * limit,
    });
  }

  // Get all audit history
  async getAuditHistory(id: number) {
    return this.userModel.findByPk(id, {
      include: ['audits']  // All changes to this user
    });
  }
}

Example Response:

{
  "id": 123,
  "name": "John Doe",
  "email": "[email protected]", 
  "creator": {
    "id": 456,
    "name": "Admin User"
  }
}

🔧 Configuration

AuditModule.forRoot() Options

interface AuditModuleOptions {
  autoSync?: boolean;           // Auto-create audit table (default: true)
  actorTypes?: string[];        // Models that can be actors (default: ['User'])
  creatorFields?: string[];     // Global creator fields (default: ['id', 'name', 'email'])
  onlyDirty?: boolean;          // Global: only log changed fields (default: false)
  auth?: {
    type?: 'passport' | 'custom';  // Auth strategy (default: 'passport')
    userProperty?: string;         // req[property] (default: 'user') 
    userIdField?: string;          // user[field] (default: 'id')
  }
}

@Auditable() Options

interface AuditableConfig {
  exclude?: string[];           // Fields to skip
  mask?: string[];              // Fields to show as '***MASKED***'
  auditEvents?: AuditEvent[];   // Which operations to track
  onlyDirty?: boolean;          // Override global dirty setting
  verbose?: boolean;            // Enable debug logging
}

🎭 Multi-Actor Support

Track different types of users automatically:

// Configure multiple actor types
AuditModule.forRoot({
  actorTypes: ['User', 'Admin', 'System'],
  // ...
})

// Different actors create different audit records
@Auditable()
export class Post extends Model {
  // Audit records will show:
  // - actorable_type: "User" | "Admin" | "System"  
  // - actorable_id: actual ID
  // - creator field resolves automatically
}

📦 Bulk Operations

Audit system automatically handles bulk operations:

// Bulk create - creates individual audit records
await User.bulkCreate([
  { name: 'John', email: '[email protected]' },
  { name: 'Jane', email: '[email protected]' },
]);

// Bulk update - captures old values automatically  
await User.update(
  { status: 'active' },
  { where: { role: 'member' } }
);

// Each affected record gets its own audit entry

⚠️ Performance Note: Bulk updates/deletes perform additional SELECT queries to capture old values. Use batching for large datasets.

🛠️ Advanced Usage

Manual Context for Background Jobs

import { RequestContext } from '@cleancode-id/nestjs-sequelize-auditor';

// Background jobs
await RequestContext.runWithContext(
  {
    actorableType: 'System',
    actorableId: 'cleanup-job',
    tags: { jobType: 'data-cleanup' }
  },
  async () => {
    await User.destroy({ where: { inactive: true } });
  }
);

Querying Audit Data

// Get all changes to a user
const userAudits = await AuditModel.findAll({
  where: {
    auditable_type: 'User',
    auditable_id: '123'
  },
  order: [['created_at', 'DESC']]
});

// Get all actions by an admin
const adminActions = await AuditModel.findAll({
  where: {
    actorable_type: 'Admin',
    actorable_id: '456'
  }
});

Conditional Auditing

// Different auditing per environment
@Auditable({
  auditEvents: process.env.NODE_ENV === 'production' 
    ? [AuditEvent.CREATED, AuditEvent.DELETED]      // Skip updates in prod
    : [AuditEvent.CREATED, AuditEvent.UPDATED, AuditEvent.DELETED]
})
export class User extends Model {}

// Security-sensitive model
@Auditable({
  mask: ['password', 'ssn'],
  auditEvents: [AuditEvent.CREATED, AuditEvent.DELETED], // No update tracking
})
export class PaymentMethod extends Model {}

📊 Database Schema

The audit table is created automatically:

CREATE TABLE audits (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  event ENUM('created', 'updated', 'deleted', 'restored') NOT NULL,
  
  -- What was changed
  auditable_type VARCHAR(255) NOT NULL,  -- "User", "Post", etc.
  auditable_id VARCHAR(255) NOT NULL,
  
  -- Who made the change  
  actorable_type VARCHAR(255),           -- "User", "Admin", "System"
  actorable_id VARCHAR(255),
  
  -- Change data
  old_values JSON,                       -- Previous state
  new_values JSON,                       -- New state
  
  -- Request context
  ip VARCHAR(45),
  user_agent TEXT,
  url VARCHAR(2048),
  tags JSON,
  
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  
  -- Indexes for performance
  INDEX idx_auditable (auditable_type, auditable_id),
  INDEX idx_actorable (actorable_type, actorable_id)
);

🚀 Why Choose This Package?

Simple Setup

  • One decorator - just add @Auditable() to your models
  • Auto-initialization - relationships created automatically
  • Zero boilerplate - no manual service setup required

Secure by Default

  • Global creator filtering - consistent data exposure control
  • Field masking - sensitive data protection
  • Request context - automatic IP/URL/agent capture

Production Ready

  • Battle-tested - comprehensive test suite
  • Performance optimized - efficient queries and indexing
  • TypeScript native - full type safety

📋 Requirements

  • Node.js 16+
  • NestJS 10+ or 11+
  • Sequelize 6+
  • sequelize-typescript 2+
  • Database: PostgreSQL or MySQL

📝 License

MIT License - see LICENSE file for details.

🙏 Acknowledgments

Built with ❤️ by Clean Code Indonesia