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 🙏

© 2025 – Pkg Stats / Ryan Hefner

panoptes-sdk

v0.2.3

Published

Panoptes SQL auditing SDK for Node.js with multi-database support, before/after data capture, and database transport

Readme

👁️ Panoptes SDK - Node.js

Enterprise-grade SQL Audit Trails for Node.js Applications

npm version npm downloads License: MIT Node.js

Automatically audit all SQL queries with rich context, before/after snapshots, and zero configuration. A modern alternative to database triggers.

📚 Complete Documentation →


🚀 Quick Start

Installation

npm install panoptes-sdk
# or
yarn add panoptes-sdk
# or
pnpm add panoptes-sdk

Basic Usage (PostgreSQL)

import { initAudit, createAuditedPostgresClient } from 'panoptes-sdk';
import { Pool } from 'pg';

// 1. Initialize audit configuration (do this once at app startup)
initAudit({
  appName: 'my-app',
  environment: 'production',
  transports: {
    enabled: ['console', 'database']
  }
});

// 2. Create your database connection
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

// 3. Wrap your client with Panoptes audit
const auditedClient = createAuditedPostgresClient(pool, {
  database: 'mydb',
  engine: 'postgres'
});

// 4. Use it like a normal client - audits are automatic!
await auditedClient.query('UPDATE users SET email = $1 WHERE id = $2',
  ['[email protected]', 123]
);
// ✅ This query is now automatically logged with full context

✨ What You Get

Panoptes SDK automatically tracks every database change in your application:

  • Automatic Audit Logging - All INSERT, UPDATE, DELETE operations logged automatically
  • 👤 User Context - Know WHO made each change (user ID, username, IP, session)
  • 📸 Before/After Snapshots - See exact data changes for compliance and debugging
  • 🗄️ Universal Database Support - Works with PostgreSQL, MySQL, MSSQL, SQLite, Oracle
  • Zero Setup - Just wrap your database client - audit table created automatically
  • 🚀 Production Ready - Async processing for minimal performance impact (<5ms overhead)
  • 📤 Flexible Output - Send logs to console, files, databases, or HTTP endpoints
  • 🔒 Compliance Ready - Perfect for GDPR, SOC 2, HIPAA audit requirements

🎯 Why Use Panoptes?

Instead of this (database triggers):

-- Complex trigger logic that's hard to maintain
CREATE TRIGGER audit_users_update
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
  -- Manual JSON building, no user context, database-specific...
END;

Just do this:

const db = createAuditedPostgresClient(pool, { database: 'mydb', engine: 'postgres' });
// That's it! All queries are now automatically audited with full context

📖 Documentation

This is the Node.js implementation of Panoptes SDK.

📚 Full Documentation

Visit https://panoptes-sdk.pages.dev/ for complete guides:


🗄️ Database Setup Examples

Panoptes SDK works with all major SQL databases. Here's how to use it with each:

PostgreSQL

import { initAudit, createAuditedPostgresClient } from 'panoptes-sdk';
import { Pool } from 'pg';

initAudit({ appName: 'my-app', environment: 'production' });

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const client = createAuditedPostgresClient(pool, {
  database: 'mydb',
  engine: 'postgres'
});

await client.query('INSERT INTO users (name, email) VALUES ($1, $2)', ['John', '[email protected]']);

MySQL

import { initAudit, createAuditedMySQLClient } from 'panoptes-sdk';
import mysql from 'mysql2/promise';

initAudit({ appName: 'my-app', environment: 'production' });

const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  database: 'mydb'
});

const client = createAuditedMySQLClient(pool, {
  database: 'mydb',
  engine: 'mysql'
});

await client.query('INSERT INTO users (name, email) VALUES (?, ?)', ['John', '[email protected]']);

MSSQL (SQL Server)

import { initAudit, createAuditedMSSQLClient } from 'panoptes-sdk';
import sql from 'mssql';

initAudit({ appName: 'my-app', environment: 'production' });

const pool = new sql.ConnectionPool({
  server: 'localhost',
  database: 'mydb',
  user: 'sa',
  password: 'password'
});
await pool.connect();

const client = createAuditedMSSQLClient(pool, {
  database: 'mydb',
  engine: 'mssql'
});

await client.query('INSERT INTO users (name, email) VALUES (@name, @email)', {
  name: 'John',
  email: '[email protected]'
});

SQLite

import { initAudit, createAuditedSQLiteClient } from 'panoptes-sdk';
import sqlite3 from 'sqlite3';

initAudit({ appName: 'my-app', environment: 'development' });

const db = new sqlite3.Database('./mydb.sqlite');

const client = createAuditedSQLiteClient(db, {
  database: 'mydb',
  engine: 'sqlite'
});

await client.query('INSERT INTO users (name, email) VALUES (?, ?)', ['John', '[email protected]']);

Oracle

import { initAudit, createAuditedOracleClient } from 'panoptes-sdk';
import oracledb from 'oracledb';

initAudit({ appName: 'my-app', environment: 'production' });

const pool = await oracledb.createPool({
  user: 'system',
  password: 'password',
  connectString: 'localhost/XEPDB1'
});

const client = createAuditedOracleClient(pool, {
  database: 'mydb',
  engine: 'oracle'
});

await client.query('INSERT INTO users (name, email) VALUES (:name, :email)', {
  name: 'John',
  email: '[email protected]'
});

💡 Framework Integration Examples

Express.js + PostgreSQL

import express from 'express';
import { initAudit, createAuditedPostgresClient, setUserContext } from 'panoptes-sdk';
import { Pool } from 'pg';

const app = express();

// Initialize Panoptes at app startup
initAudit({
  appName: 'my-express-app',
  environment: process.env.NODE_ENV,
  transports: {
    enabled: ['console', 'database']
  }
});

// Create audited database client
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = createAuditedPostgresClient(pool, {
  database: 'mydb',
  engine: 'postgres'
});

// Middleware to set user context for ALL requests
app.use((req, res, next) => {
  setUserContext({
    userId: req.user?.id,
    username: req.user?.email,
    ipAddress: req.ip,
    sessionId: req.sessionID,
    userAgent: req.get('user-agent')
  });
  next();
});

// Your routes now automatically log all database changes
app.post('/users', async (req, res) => {
  const { name, email } = req.body;

  // This query is automatically audited with user context!
  const result = await db.query(
    'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
    [name, email]
  );

  res.json(result.rows[0]);
});

app.put('/users/:id', async (req, res) => {
  const { id } = req.params;
  const { email } = req.body;

  // Update is audited with before/after snapshots
  await db.query(
    'UPDATE users SET email = $1 WHERE id = $2',
    [email, id]
  );

  res.json({ success: true });
});

app.listen(3000);

Fastify + MySQL

import Fastify from 'fastify';
import { initAudit, createAuditedMySQLClient, setUserContext } from 'panoptes-sdk';
import mysql from 'mysql2/promise';

const fastify = Fastify();

initAudit({
  appName: 'my-fastify-app',
  environment: 'production'
});

const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME
});

const db = createAuditedMySQLClient(pool, {
  database: 'mydb',
  engine: 'mysql'
});

// Hook to set user context
fastify.addHook('onRequest', async (request, reply) => {
  setUserContext({
    userId: request.user?.id,
    username: request.user?.username,
    ipAddress: request.ip
  });
});

fastify.post('/products', async (request, reply) => {
  const { name, price } = request.body;

  const [result] = await db.query(
    'INSERT INTO products (name, price) VALUES (?, ?)',
    [name, price]
  );

  return { id: result.insertId, name, price };
});

await fastify.listen({ port: 3000 });

NestJS + PostgreSQL

// database.module.ts
import { Module } from '@nestjs/common';
import { initAudit, createAuditedPostgresClient } from 'panoptes-sdk';
import { Pool } from 'pg';

@Module({
  providers: [
    {
      provide: 'DATABASE_CLIENT',
      useFactory: () => {
        initAudit({
          appName: 'my-nestjs-app',
          environment: process.env.NODE_ENV
        });

        const pool = new Pool({
          connectionString: process.env.DATABASE_URL
        });

        return createAuditedPostgresClient(pool, {
          database: 'mydb',
          engine: 'postgres'
        });
      }
    }
  ],
  exports: ['DATABASE_CLIENT']
})
export class DatabaseModule {}

// user.service.ts
import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class UserService {
  constructor(@Inject('DATABASE_CLIENT') private db: any) {}

  async createUser(name: string, email: string) {
    const result = await this.db.query(
      'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
      [name, email]
    );
    return result.rows[0];
  }

  async updateUser(id: number, email: string) {
    await this.db.query(
      'UPDATE users SET email = $1 WHERE id = $2',
      [email, id]
    );
  }
}

⚙️ Configuration Examples

Basic Configuration

import { initAudit } from 'panoptes-sdk';

initAudit({
  appName: 'my-app',
  environment: 'production'
});

Environment-Based Configuration

initAudit({
  appName: 'my-app',
  environment: process.env.NODE_ENV,

  // Use different transports based on environment
  transports: {
    enabled: process.env.NODE_ENV === 'production'
      ? ['database', 'http']  // Production: save to DB and send to external service
      : ['console']            // Development: just log to console
  },

  // Enable async mode in production for better performance
  asyncMode: process.env.NODE_ENV === 'production',
  bufferSize: 500
});

Audit Only Specific Tables

initAudit({
  appName: 'my-app',

  // Only audit these tables
  rules: {
    includeTables: ['users', 'transactions', 'orders', 'payments'],
    operations: ['INSERT', 'UPDATE', 'DELETE']  // Don't audit SELECT
  }
});

Exclude Sensitive Tables

initAudit({
  appName: 'my-app',

  rules: {
    excludeTables: ['sessions', 'cache', 'temp_data'],  // Skip these tables
    operations: ['INSERT', 'UPDATE', 'DELETE']
  }
});

Multiple Transport Destinations

initAudit({
  appName: 'my-app',

  transports: {
    enabled: ['console', 'database', 'file', 'http'],

    // File transport configuration
    file: {
      path: './logs/audit.log',
      maxSize: '100MB',
      maxFiles: 10
    },

    // HTTP transport (send to external service)
    http: {
      url: 'https://your-logging-service.com/api/logs',
      headers: {
        'Authorization': `Bearer ${process.env.LOGGING_API_KEY}`
      }
    }
  }
});

Custom Metadata in Context

import { setUserContext } from 'panoptes-sdk';

// Set context with custom fields
setUserContext({
  userId: user.id,
  username: user.email,
  ipAddress: req.ip,

  // Add custom metadata
  metadata: {
    organizationId: user.organizationId,
    department: user.department,
    role: user.role,
    requestId: req.headers['x-request-id']
  }
});

Async Mode for High Performance

initAudit({
  appName: 'my-app',

  // Process audits asynchronously (recommended for production)
  asyncMode: true,
  bufferSize: 1000,  // Buffer up to 1000 audit logs before flushing

  // Flush interval (in milliseconds)
  flushInterval: 5000  // Flush every 5 seconds
});

📊 Reading Audit Logs

Once you start using Panoptes, all audits are stored in the audit_logs table. Here's how to query them:

View Recent Audits

SELECT
  operation,
  table_name,
  user_id,
  username,
  ip_address,
  created_at,
  query
FROM audit_logs
ORDER BY created_at DESC
LIMIT 50;

Find All Changes by a User

SELECT *
FROM audit_logs
WHERE user_id = 123
ORDER BY created_at DESC;

Track Changes to Specific Record

SELECT
  operation,
  before_state,
  after_state,
  username,
  created_at
FROM audit_logs
WHERE table_name = 'users'
  AND query LIKE '%WHERE id = 123%'
ORDER BY created_at DESC;

Audit Trail for Compliance

-- Find all deletions in the last 30 days
SELECT
  table_name,
  before_state,
  username,
  ip_address,
  created_at
FROM audit_logs
WHERE operation = 'DELETE'
  AND created_at > NOW() - INTERVAL '30 days'
ORDER BY created_at DESC;

➡️ More Examples in Documentation


🔍 API Reference

Main Functions

initAudit(config)

Initializes Panoptes audit system. Call this once at application startup.

initAudit({
  appName: 'my-app',              // Required: Your application name
  environment: 'production',       // Required: Environment (dev, staging, prod, etc.)

  // Optional configurations
  asyncMode: true,                 // Process audits asynchronously
  bufferSize: 1000,               // Buffer size for async mode
  flushInterval: 5000,            // Flush interval in ms

  rules: {
    includeTables: ['users'],     // Only audit these tables
    excludeTables: ['cache'],     // Skip these tables
    operations: ['INSERT', 'UPDATE', 'DELETE']  // Which operations to audit
  },

  transports: {
    enabled: ['console', 'database', 'file', 'http'],
    file: { path: './logs/audit.log' },
    http: { url: 'https://api.example.com/logs' }
  }
});

createAuditedPostgresClient(pool, config)

Creates an audited PostgreSQL client.

const client = createAuditedPostgresClient(pool, {
  database: 'mydb',
  engine: 'postgres'
});

createAuditedMySQLClient(pool, config)

Creates an audited MySQL client.

const client = createAuditedMySQLClient(pool, {
  database: 'mydb',
  engine: 'mysql'
});

createAuditedMSSQLClient(pool, config)

Creates an audited MSSQL client.

const client = createAuditedMSSQLClient(pool, {
  database: 'mydb',
  engine: 'mssql'
});

createAuditedSQLiteClient(db, config)

Creates an audited SQLite client.

const client = createAuditedSQLiteClient(db, {
  database: 'mydb',
  engine: 'sqlite'
});

createAuditedOracleClient(pool, config)

Creates an audited Oracle client.

const client = createAuditedOracleClient(pool, {
  database: 'mydb',
  engine: 'oracle'
});

setUserContext(context)

Sets user context for the current request/operation. Call this in your middleware.

setUserContext({
  userId: 123,                    // User ID
  username: '[email protected]',   // Username or email
  ipAddress: '192.168.1.1',       // IP address
  sessionId: 'abc123',            // Session ID
  userAgent: 'Mozilla/5.0...',    // User agent

  // Custom metadata
  metadata: {
    organizationId: 456,
    role: 'admin'
  }
});

📦 Package Information

  • Version: 0.2.0
  • License: MIT
  • Node.js: >=18
  • Module Types: ESM + CommonJS
  • TypeScript: Full type definitions included
  • Size: ~50KB (minified)

❓ FAQ

Do I need to create the audit table manually?

No! Panoptes automatically creates the audit_logs table on first run.

Will this slow down my queries?

In async mode (recommended for production), auditing happens in the background with minimal performance impact. Typical overhead is <5ms per query.

Can I use this with ORMs like Prisma or TypeORM?

Yes, but you'll need to wrap the underlying database connection. See the Integrations guide for examples.

How do I handle migrations?

The audit table is created automatically. For schema changes, see the Migration guide.

Can I customize the audit table name?

Yes, you can configure this in initAudit():

initAudit({
  appName: 'my-app',
  tableName: 'my_custom_audit_table'
});

📄 License

MIT License - Free for personal and commercial use.


🔗 Useful Links


🆘 Support

Need help? Here's how to get support:

  1. Check the Documentation - Most common questions are answered there
  2. Search GitHub Issues - Someone may have already solved your problem
  3. Ask in Discussions - Community support
  4. Open an Issue - For bugs or feature requests

Made by malydev

If Panoptes SDK helps your project, consider giving it a ⭐ on GitHub!

📚 Read Full Documentation🐛 Report Bug💡 Request Feature