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

typeorm-query-hooks

v6.5.1

Published

πŸš€ Powerful plugin-based hooks for TypeORM: Performance monitoring, result validation, query modification, transaction tracking & more. Works with JS, TS & NestJS!

Readme

πŸš€ TypeORM Query Hooks

The ultimate TypeORM companion - 21 powerful plugins to prevent N+1 queries, detect connection leaks, block dangerous operations, auto-run EXPLAIN, trace query sources & more. Works seamlessly with JavaScript, TypeScript, and NestJS.

npm version License: MIT TypeScript


✨ Why Use This Library?

Prevents Production Disasters:

  • πŸ•΅οΈ N+1 Query Detection - Catches the #1 performance killer automatically
  • πŸ›‘οΈ Safety Guards - Blocks DELETE/UPDATE without WHERE, prevents DDL in production
  • πŸ’§ Connection Leak Detection - Finds leaks before they crash your app
  • 🧟 Zombie Transaction Monitoring - Prevents deadlocks from idle transactions

Automatic Debugging:

  • πŸ“ Source Code Tracing - Shows exact file:line where queries originate (no more guessing!)
  • πŸ”¬ Auto-EXPLAIN - Runs query plan analysis on slow queries automatically
  • ⚠️ Lazy Loading Detection - Catches hidden N+1 problems

Enterprise Features:

  • πŸ“ Audit Logging - GDPR/HIPAA compliance ready
  • πŸ—‘οΈ Cache Invalidation - Auto-invalidate on data changes
  • πŸ”„ Result Transformation - Auto-convert to DTOs, remove sensitive data

Extensible:

  • 🎨 Create Custom Plugins - Build your own hooks for specific needs
  • πŸ”Œ 21 Built-in Plugins - Ready-to-use solutions for common problems
  • 🎯 Full TypeScript Support - Type-safe plugin development with exported types

πŸ—οΈ 21 Powerful Plugins Included

πŸ“š Event Callbacks: All plugins support event callbacks for custom handling. See PLUGIN_CALLBACKS_REFERENCE.md for complete callback documentation.

πŸ’‘ TypeScript: All callback parameter types are exported and can be imported:

import { 
  QueryExecutionContext, 
  ConnectionLeak, 
  BlockedOperation 
} from 'typeorm-query-hooks';

πŸ”₯ Critical Performance & Safety

| Plugin | Purpose | Use Case | |--------|---------|----------| | πŸ•΅οΈ NPlusOneDetector | Detect N+1 query problems | #1 performance killer - catches 80% of issues | | πŸ›‘οΈ SafetyGuard | Block dangerous operations | Prevents DELETE/UPDATE without WHERE, blocks DDL | | πŸ’§ ConnectionLeakDetector | Find connection leaks | Prevents pool exhaustion and app crashes | | ⏱️ QueryTimeout | Automatic query timeouts | Prevents queries from hanging forever | | 🧟 IdleTransactionMonitor | Detect zombie transactions | Prevents deadlocks from idle transactions |

πŸ”¬ Analysis & Debugging

| Plugin | Purpose | Use Case | |--------|---------|----------| | πŸ“ QuerySourceTracer | Show where queries originate | CSI: Database - find exact file:line in your code | | πŸ”¬ SlowQueryAnalyzer | Auto-run EXPLAIN on slow queries | Automatic query plan analysis | | ⚠️ LazyLoadingDetector | Detect lazy-loaded relations | Catches hidden N+1 problems | | ⚑ PerformanceMonitor | Track query execution time | Monitor and optimize performance |

πŸ—ƒοΈ Data Management

| Plugin | Purpose | Use Case | |--------|---------|----------| | πŸ—‘οΈ CacheInvalidation | Auto-invalidate cache on writes | Maintain cache consistency | | πŸ“ AuditLogging | Track all database operations | Compliance (GDPR, HIPAA), security | | πŸ“Š BulkOperations | Detect bulk operations | Prevent accidental mass updates | | πŸ”„ QueryResultTransformer | Transform query results | Auto-convert to DTOs, remove sensitive data |

πŸ› οΈ Utilities

| Plugin | Purpose | Use Case | |--------|---------|----------| | 🏷️ TableExtractor | Extract table names from queries | Logging, caching, access control | | πŸ”€ QueryTypeDetector | Detect SQL query types (SELECT, INSERT, etc.) | Analytics, monitoring, security | | βœ… ResultValidator | Validate query results | Alert on empty results, pagination issues | | ✏️ QueryModifier | Modify queries before execution | Multi-tenancy, query hints, safety | | πŸ” QueryComplexity | Warn on complex queries | Identify queries needing optimization | | πŸ’Ύ QueryMetadataRegistry | Store query metadata | Analytics, cross-cutting concerns | | πŸͺ΅ QueryLogger | Custom query logging | Flexible logging with filters |


πŸ“¦ Installation

npm install typeorm-query-hooks
# or
yarn add typeorm-query-hooks

⚑ Quick Start

1. Enable Hooks

import { enableQueryHooks } from 'typeorm-query-hooks';

// Enable at application startup (before any TypeORM queries)
enableQueryHooks({ 
  verbose: false  // Set to true for debugging
});

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | verbose | boolean | false | Enable detailed debug logging for the core hook system. Shows: when hooks fire, which plugins execute, query capture events, context propagation, errors. Very noisy - use only for troubleshooting plugin issues or understanding hook flow. Never enable in production. |

When to use verbose: true:

  • Debugging why a plugin isn't working
  • Understanding the hook execution flow
  • Development/testing only (too noisy for production)

Note: This is the only configuration for enableQueryHooks(). All other configurations are plugin-specific.


2. Register Plugins

import { registerPlugin } from 'typeorm-query-hooks';
import { NPlusOneDetectorPlugin } from 'typeorm-query-hooks/plugins/n-plus-one-detector';

// Register any plugins you need
registerPlugin(NPlusOneDetectorPlugin({
  threshold: 5,
  enableLogging: true
}));

πŸ—οΈ Built-in Plugins (20 Total!)

πŸ”₯ Critical Performance & Safety

πŸ•΅οΈ NPlusOneDetector - Detect N+1 query problems (THE #1 performance killer)

What it does: Detects when the same query runs repeatedly in a short time window - the classic N+1 problem.

The Problem:

// ❌ BAD - Causes N+1 problem
const users = await userRepository.find();  // 1 query
for (const user of users) {  // Loop
  const posts = await postRepository.find({ where: { userId: user.id } });  // N queries!
}
// Total: 101 queries for 100 users!

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | threshold | number | 5 | How many times the same query can run in the time window before flagging as N+1. Lower threshold (3-5) catches more issues. Higher (10+) only catches severe cases. | | window | number | 100 | Time window (ms) to track query patterns. 100ms catches most N+1 loops. Increase to 500-1000ms for batch jobs that intentionally space out queries. | | includeStackTrace | boolean | true | Capture stack trace showing WHERE in your code the N+1 originated. Performance impact: ~1-2ms per query. Essential for fixing N+1 issues - shows exact loop location. | | ignorePatterns | RegExp[] | [] | Regex patterns to exclude from N+1 detection. Example: [/migrations/, /@nestjs/] ignores framework code. Use to reduce false positives from legitimate repeated queries. | | enableLogging | boolean | false | Log N+1 warnings to console with query fingerprint and stack trace. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onNPlusOneDetected | (context: NPlusOneContext, pattern: QueryPattern) | Called when N+1 pattern is detected |

Usage:

import { NPlusOneDetectorPlugin } from 'typeorm-query-hooks/plugins/n-plus-one-detector';

registerPlugin(NPlusOneDetectorPlugin({
  threshold: 5,        // Flag if same query runs > 5 times
  window: 100,         // Within 100ms window
  includeStackTrace: true,
  enableLogging: true,
  onNPlusOneDetected: (context, count, fingerprint) => {
    logger.error(`🚨 N+1 DETECTED! Query ran ${count} times`, {
      fingerprint: fingerprint.substring(0, 100),
      suggestion: 'Use .leftJoinAndSelect() or relations: []'
    });
    
    // Send to monitoring
    datadog.increment('n_plus_one_detected', { count });
  }
}));

πŸ›‘οΈ SafetyGuard - Block dangerous database operations

What it does: Prevents catastrophic mistakes like UPDATE users SET role='admin' (no WHERE = ALL users become admin!)

Real disasters this prevents:

  • Junior dev ran UPDATE users SET email='[email protected]' without WHERE β†’ 1M users had same email
  • Migration with DROP TABLE ran in production
  • DELETE FROM orders without WHERE β†’ Lost 6 months of data

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | blockDDL | boolean | false | Block DDL (Data Definition Language) operations: CREATE, ALTER, DROP, TRUNCATE. Enable in production to prevent schema changes. | | requireWhereClause | boolean | true | ⚠️ CRITICAL - Blocks UPDATE/DELETE without WHERE clause. Prevents accidentally modifying ALL rows in a table. Should always be true in production. | | blockTruncate | boolean | true | Block TRUNCATE operations which delete all rows and reset auto-increment. More dangerous than DELETE as it can't be rolled back in some databases. | | blockDrop | boolean | true | Block DROP TABLE/DATABASE operations. Prevents accidental data loss from schema deletions. | | allowedEnvironments | string[] | ['development','test'] | Environments where destructive operations are permitted. Checks process.env.NODE_ENV. Production should NOT be in this list. | | protectedTables | string[] | [] | Tables requiring extra protection beyond normal rules. Operations on these tables ALWAYS require WHERE clause, even in dev. Example: ['users', 'payments', 'audit_logs'] | | allowForce | boolean | false | Allow developers to bypass safety checks by adding /* FORCE_ALLOW */ comment to SQL. Use in dev/staging only, NEVER in production. Example: /* FORCE_ALLOW */ DELETE FROM temp_data | | throwOnBlock | boolean | true | When true: Throws error and stops query execution. When false: Logs warning but allows query to proceed. Set to true in production for maximum safety. | | enableLogging | boolean | false | Log blocked operations to console. Useful for monitoring what operations are being prevented. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onBlocked | (context: PreQueryContext, blocked: BlockedOperation) | Called when dangerous operation is blocked |

Usage:

import { SafetyGuardPlugin } from 'typeorm-query-hooks/plugins/safety-guard';

// Recommended for production
registerPlugin(SafetyGuardPlugin({
  blockDDL: process.env.NODE_ENV === 'production',
  requireWhereClause: true,  // ALWAYS require WHERE
  protectedTables: ['users', 'payments', 'transactions'],
  throwOnBlock: true,
  onBlocked: (context, blocked) => {
    // Send critical alert
    pagerduty.trigger({
      severity: 'critical',
      summary: `Dangerous operation blocked: ${blocked.operation}`,
      tables: blocked.tables
    });
  },
  enableLogging: true
}));

πŸ’§ ConnectionLeakDetector - Find connection leaks before they crash your app

What it does: Detects connections that are acquired but never released - leads to pool exhaustion.

The Problem:

const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.query('SELECT ...');
// ❌ FORGOT queryRunner.release() - connection leaked!

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | maxConnectionAge | number | 30000 | Maximum time (ms) a connection can remain acquired before being flagged as leaked. 30 seconds is usually enough for any query. Increase for long-running analytics queries. | | warnThreshold | number | 0.8 | Percentage of pool capacity (0-1) that triggers early warning. 0.8 = warns at 80% full. Helps catch issues before complete pool exhaustion. | | captureStackTrace | boolean | true | Captures stack trace when connection is acquired to show WHERE the leak originated. Performance impact: ~1-2ms per connection. Disable in high-throughput production if needed. | | enableLogging | boolean | false | Auto-log leak warnings to console with stack traces. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onLeak | (leak: ConnectionLeak) | Called when connection leak is detected | | onPoolWarning | (context: ConnectionPoolContext) | Called when pool capacity warning triggered |

Usage:

import { ConnectionLeakDetectorPlugin } from 'typeorm-query-hooks/plugins/connection-leak-detector';

registerPlugin(ConnectionLeakDetectorPlugin({
  maxConnectionAge: 30000,  // 30 seconds
  warnThreshold: 0.8,       // Warn at 80% pool capacity
  captureStackTrace: true,
  enableLogging: true,
  onLeak: (leak) => {
    logger.error('πŸ’§ CONNECTION LEAK:', {
      age: `${leak.age}ms`,
      stackTrace: leak.stackTrace
    });
    monitoring.alert({ type: 'connection_leak', severity: 'critical' });
  }
}));

⏱️ QueryTimeout - Automatic query timeouts

What it does: Prevents queries from hanging forever and blocking the connection pool.

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | defaultTimeout | number | 5000 | Default timeout (ms) for all queries. Prevents queries from hanging indefinitely and blocking connections. 5 seconds is reasonable for OLTP workloads. | | timeoutByType | Record<string, number> | {} | Override timeout by operation type. Example: { 'SELECT': 3000, 'INSERT': 10000 }. Useful for setting stricter limits on reads vs writes. | | timeoutByTablePattern | Record<string, number> | {} | Override timeout using regex patterns for table names. Example: { 'report_.*': 30000, 'analytics_.*': 60000 }. Allows longer timeouts for known slow tables. | | throwOnTimeout | boolean | true | When true: Throws error to cancel query. When false: Logs warning but lets query continue (not recommended - query will still hold connection). | | warningThreshold | number | 0.8 | Triggers early warning callback at percentage of timeout (0.8 = 80%). Allows proactive logging before actual timeout. Set to 0 to disable warnings. | | enableLogging | boolean | false | Auto-log timeout events to console with query details. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onTimeout | (context: QueryExecutionContext, timeout: number) | Called when query times out | | onTimeoutWarning | (context: QueryExecutionContext, elapsed: number, limit: number) | Called when query approaches timeout | | onError | (context: QueryExecutionContext, error: Error) | Called when timeout mechanism fails |

Usage:

import { QueryTimeoutPlugin } from 'typeorm-query-hooks/plugins/query-timeout';

registerPlugin(QueryTimeoutPlugin({
  defaultTimeout: 5000,
  timeoutByType: {
    'SELECT': 3000,   // Reads should be fast
    'INSERT': 10000,  // Writes can be slower
    'UPDATE': 10000
  },
  timeoutByTablePattern: {
    'report_.*': 30000,    // Reports can take 30s
    'analytics_.*': 60000  // Analytics 60s
  },
  throwOnTimeout: true,
  enableLogging: true
}));

🧟 IdleTransactionMonitor - Detect zombie transactions

What it does: Detects transactions that sit idle (no queries running) - causes deadlocks.

The Problem:

await queryRunner.startTransaction();
await queryRunner.manager.save(user);

// ❌ Transaction is OPEN while doing HTTP call!
await fetch('https://api.slow-service.com');  // 5 seconds

// Meanwhile: DB locks held, other queries waiting, deadlock risk
await queryRunner.commitTransaction();

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | maxTransactionDuration | number | 5000 | Maximum total transaction duration (ms) from BEGIN to COMMIT/ROLLBACK. Long transactions hold locks and block other queries. 5 seconds is safe for OLTP. | | maxIdleTime | number | 1000 | Maximum idle time (ms) with no queries executing inside a transaction. Detects "zombie" transactions (open but doing nothing). 1 second catches most issues. | | autoRollback | boolean | false | ⚠️ DANGEROUS - Automatically rollback transactions exceeding limits. Why dangerous: May rollback legitimate long-running operations (reports, migrations, batch jobs). Recommendation: Keep false in production, use onZombieDetected callback for alerts instead. Only enable in dev/test environments. | | enableLogging | boolean | false | Log zombie transaction warnings to console with duration and idle time. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onZombieDetected | (context: TransactionContext, zombie: ZombieTransaction) | Called when zombie transaction detected (long-running AND idle) | | onLongRunningTransaction | (context: TransactionContext, duration: number) | Called when transaction exceeds max duration | | onIdleTransaction | (context: TransactionContext, idleTime: number) | Called when transaction is idle too long | | onError | (context: TransactionContext \| undefined, error: Error) | Called when monitoring fails |

Usage:

import { IdleTransactionMonitorPlugin } from 'typeorm-query-hooks/plugins/idle-transaction-monitor';

registerPlugin(IdleTransactionMonitorPlugin({
  maxTransactionDuration: 5000,
  maxIdleTime: 1000,
  autoRollback: false,  // Don't auto-rollback in production
  enableLogging: true,
  onZombieDetected: (context, zombie) => {
    logger.error('🧟 ZOMBIE TRANSACTION:', {
      duration: `${zombie.duration}ms`,
      idleTime: `${zombie.idleTime}ms`,
      queries: zombie.queriesExecuted
    });
  }
}));

πŸ”¬ Analysis & Debugging

πŸ“ QuerySourceTracer - CSI: Database (find exact file:line in your code)

What it does: Shows you EXACTLY where in your code each query originated.

The Problem: You see a slow query: SELECT * FROM users WHERE email = '...' You have 50 places that query users. Which one is slow? You don't know!

The Solution: Shows: Query from: src/services/UserService.ts:45:12 in UserService.findByEmail

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | basePath | string | process.cwd() | Base directory for relative path resolution. Stack traces show paths relative to this. Default is project root. Set to process.cwd() + '/src' to show only source files. | | attachToQueryContext | boolean | true | When true, adds context.sourceLocation to query context so other plugins (PerformanceMonitor, NPlusOneDetector, etc.) can access file:line info. Keep enabled for better debugging. | | includeFullStackTrace | boolean | false | When true, captures entire stack trace (20+ frames). When false, captures only first relevant frame. Enable for deep debugging, disable for cleaner logs. | | ignorePaths | string[] | ['node_modules'] | Array of path patterns to skip when tracing. Filters out framework code to show only YOUR code. Example: ['node_modules', 'dist', '@nestjs'] | | enableLogging | boolean | false | Log source location for every query. Very verbose - use only for debugging specific issues. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onQueryLogged | (context: QueryContextWithSource, location: SourceLocation) | Called when query source is traced |

Usage:

import { QuerySourceTracerPlugin } from 'typeorm-query-hooks/plugins/query-source-tracer';

registerPlugin(QuerySourceTracerPlugin({
  basePath: process.cwd() + '/src',
  attachToQueryContext: true,  // Other plugins can use context.sourceLocation
  enableLogging: true
}));

// Logs show:
// [QuerySourceTracer] πŸ“ Query Source:
//   File: src/services/UserService.ts
//   Line: 45:12
//   Function: UserService.findByEmail
//   SQL: SELECT "user"."id", "user"."email" FROM "users"...

πŸ”¬ SlowQueryAnalyzer - Auto-run EXPLAIN on slow queries

What it does: Automatically runs EXPLAIN (or EXPLAIN ANALYZE) on slow queries to show you WHY they're slow.

The Manual Way (painful):

  1. Slow query alert fires
  2. Copy the SQL
  3. Open pgAdmin/DBeaver
  4. Paste and run EXPLAIN ANALYZE
  5. Look for issues

The Automatic Way: Plugin does it all automatically and logs the execution plan immediately!

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | threshold | number | 1000 | Trigger EXPLAIN analysis for queries slower than this (ms). Helps identify which slow queries need optimization. Lower threshold = more analysis but more overhead. | | runAnalyze | boolean | false | ⚠️ USE WITH CAUTION - EXPLAIN ANALYZE actually executes the query twice: once for real results, once for analysis. Risks: (1) Write operations (INSERT/UPDATE/DELETE) will modify data twice, (2) Doubles query time, (3) Side effects happen twice. Safe for: SELECT queries in non-production. Never use for: Production, or any INSERT/UPDATE/DELETE. | | databaseType | string | 'postgres' | Database type determines EXPLAIN syntax. Supported: 'postgres', 'mysql', 'mariadb', 'sqlite', 'mssql'. Each database has different EXPLAIN output format. | | enableLogging | boolean | false | Log EXPLAIN results to console. Shows query plans with cost estimates, index usage, and scan types. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onAnalysis | (context: QueryExecutionContext, plan: QueryExecutionPlan) | Called when EXPLAIN analysis completes |

Usage:

import { SlowQueryAnalyzerPlugin } from 'typeorm-query-hooks/plugins/slow-query-analyzer';

registerPlugin(SlowQueryAnalyzerPlugin({
  threshold: 1000,
  databaseType: 'postgres',
  enableLogging: true,
  onAnalysis: (context, plan) => {
    if (plan.hasSeqScan) {
      logger.error('πŸ” MISSING INDEX DETECTED:', {
        sql: context.sql.substring(0, 200),
        executionTime: context.executionTime,
        plan: plan.raw,
        suggestion: 'Add an index to improve performance'
      });
    }
  }
}));

⚠️ LazyLoadingDetector - Detect lazy-loaded relations (hidden N+1)

What it does: Warns when lazy-loaded relations are accessed (often causes hidden N+1 queries).

The Problem:

@Entity()
class User {
  @OneToMany(() => Post, post => post.user)
  posts: Promise<Post[]>;  // Lazy loaded!
}

// Usage - looks innocent but causes N+1
const users = await userRepo.find();
for (const user of users) {
  const posts = await user.posts;  // Separate query per user!
}

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | warnOnLazyLoad | boolean | true | ⚠️ Deprecated - Use onLazyLoadDetected callback instead for custom handling | | suggestEagerLoading | boolean | true | When true, includes code suggestions in warnings showing how to fix with eager loading (relations: [] or .leftJoinAndSelect()). Helpful for developers learning TypeORM. | | threshold | number | 1 | Number of times same relation can be lazy-loaded before triggering warning. Set to 3-5 to ignore isolated cases and focus on true N+1 patterns. Set to 1 to catch every lazy load. | | enableLogging | boolean | false | Log lazy loading warnings to console with suggestions. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onLazyLoadDetected | (context: QueryHookContext, relationName: string, count: number) | Called when lazy loading pattern is detected | | onError | (context: QueryHookContext, error: Error) | Called when detection fails |

Usage:

import { LazyLoadingDetectorPlugin } from 'typeorm-query-hooks/plugins/lazy-loading-detector';

registerPlugin(LazyLoadingDetectorPlugin({
  warnOnLazyLoad: true,
  suggestEagerLoading: true,
  threshold: 3,
  enableLogging: true
}));

// Shows suggestions like:
// ⚠️ Potential lazy loading detected
// πŸ’‘ Suggestion: Use eager loading:
//   - Option 1: find({ relations: ['posts'] })
//   - Option 2: .leftJoinAndSelect('user.posts', 'posts')

⚑ PerformanceMonitor - Track query execution time

What it does: Monitors query performance and detects slow queries.

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | slowQueryThreshold | number | 500 | Queries exceeding this duration (ms) are flagged as "slow" and trigger onSlowQuery callback. Typical values: 100ms for user-facing queries, 500ms for background jobs, 1000ms for reports. | | enableLogging | boolean | false | Log performance metrics for every query (duration, SQL, tables). Can be verbose. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onSlowQuery | (context: QueryExecutionContext) | Called when query exceeds slowQueryThreshold | | onMetric | (context: QueryExecutionContext) | Called for all query completions (for custom metrics) |

Usage:

import { PerformanceMonitorPlugin } from 'typeorm-query-hooks/plugins/performance-monitor';

registerPlugin(PerformanceMonitorPlugin({
  slowQueryThreshold: 300,
  enableLogging: true,
  onSlowQuery: (context) => {
    datadog.histogram('db.query.duration', context.executionTime);
  },
  onMetric: (context) => {
    prometheus.histogram('query_duration', context.executionTime);
  }
}));

πŸ—ƒοΈ Data Management

πŸ—‘οΈ CacheInvalidation - Auto-invalidate cache on data changes

What it does: Automatically invalidates cache when INSERT, UPDATE, or DELETE operations occur.

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | invalidateOnTypes | string[] | ['INSERT','UPDATE','DELETE'] | Query types that trigger cache invalidation. SELECT doesn't invalidate. Add 'TRUNCATE' if needed. Remove types to skip invalidation for certain operations. | | monitorTables | string[] | [] (all) | Limit monitoring to specific tables. Example: ['users', 'products'] only invalidates cache for these tables. Empty array = monitor ALL tables. Use to avoid invalidating rarely-cached tables. | | enableLogging | boolean | false | Log every cache invalidation (table names, operation type). |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onInvalidate | (tables: string[], context: QueryExecutionContext) | ⚠️ REQUIRED - Called to clear your cache (Redis, memory, etc.) |

Usage:

import { CacheInvalidationPlugin } from 'typeorm-query-hooks/plugins/cache-invalidation';

registerPlugin(CacheInvalidationPlugin({
  onInvalidate: async (tables) => {
    for (const table of tables) {
      await redis.del(`cache:${table}:*`);
    }
  },
  monitorTables: ['users', 'products'],  // Only these tables
  enableLogging: true
}));

πŸ“ AuditLogging - Track all database operations (GDPR/HIPAA ready)

What it does: Comprehensive audit trail of who did what, when, and on which tables.

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | auditTypes | string[] | ['INSERT','UPDATE','DELETE'] | Query types to audit. Typically only data modifications. Add 'SELECT' to audit reads (verbose). | | auditTables | string[] | [] (all) | Limit auditing to specific tables containing sensitive data. Example: ['users', 'payments', 'medical_records']. Empty = audit ALL tables (can be storage-intensive). | | includeSql | boolean | true | Include full SQL statement in audit logs. Useful for forensics but increases log size. | | includeParameters | boolean | false | ⚠️ SECURITY RISK - Include query parameters (actual values) in audit logs. May expose passwords, PII, credit cards. Only enable if logs are encrypted and access-controlled. Recommended: keep false for GDPR/HIPAA compliance. | | metadata | object\|function | undefined | Additional data to include in every audit entry. Object: static values like { app: 'api', version: '1.0' }. Function: dynamic values like () => ({ requestId: getRequestId(), ip: getClientIP() }). | | enableLogging | boolean | false | Log audit entries to console (in addition to onAudit callback). |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onAudit | (entry: AuditLogEntry) | ⚠️ REQUIRED - Called to persist audit logs | | getUserId | () => string \| number \| undefined | Called to get current user ID |

Usage:

import { AuditLoggingPlugin } from 'typeorm-query-hooks/plugins/audit-logging';

registerPlugin(AuditLoggingPlugin({
  getUserId: () => getCurrentUser()?.id,
  onAudit: async (entry) => {
    await auditLogRepository.save({
      userId: entry.userId,
      action: entry.action,
      tables: entry.tables,
      timestamp: entry.timestamp,
      success: entry.success
    });
  },
  auditTypes: ['INSERT', 'UPDATE', 'DELETE'],
  auditTables: ['users', 'financial_records'],  // Sensitive tables only
  includeSql: true,
  includeParameters: false,  // Don't log sensitive data
  metadata: () => ({ 
    environment: process.env.NODE_ENV,
    requestId: getRequestId() 
  }),
  enableLogging: true
}));

πŸ“Š BulkOperations - Prevent accidental mass updates/deletes

What it does: Detects operations affecting many rows - prevents accidents like deleting 100,000 records.

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | bulkThreshold | number | 100 | Number of rows affected to consider an operation "bulk". Adjust based on your app: 50 for user-facing tables, 1000+ for analytics tables. Prevents accidental mass operations. | | monitorTypes | string[] | ['INSERT','UPDATE','DELETE'] | Query types to monitor. Bulk SELECTs are rarely dangerous (just slow), so usually only monitor data modifications. | | monitorTables | string[] | [] (all) | Limit monitoring to specific tables. Example: ['users', 'orders'] to protect critical tables. Empty = monitor all tables. | | warnOnBulk | boolean | true | ⚠️ Deprecated - Use onBulkOperation callback instead for custom handling | | enableLogging | boolean | false | Log bulk operation warnings to console with affected row count. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onBulkOperation | (context: QueryResultContext, affectedRows: number) | Called when operation exceeds bulkThreshold |

Usage:

import { BulkOperationsPlugin } from 'typeorm-query-hooks/plugins/bulk-operations';

registerPlugin(BulkOperationsPlugin({
  bulkThreshold: 50,
  warnOnBulk: true,
  enableLogging: true,
  onBulkOperation: (context, affectedRows) => {
    if (process.env.NODE_ENV === 'production') {
      throw new Error(`Bulk operation blocked: ${affectedRows} rows`);
    }
  }
}));

πŸ”„ QueryResultTransformer - Auto-transform results to DTOs

What it does: Automatically transforms database results to DTOs, removes sensitive data, adds computed fields.

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | enableLogging | boolean | false | Auto-log transformations |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | transformers | Record<string, TransformerFn> | Transform results by entity/table name | | globalTransformer | (result: any, context: QueryResultContext) => any | Applied to all results before entity transformers | | onTransformed | (context: QueryResultContext, originalResult: any, transformedResult: any) | Called when result was transformed | | onError | (context: QueryResultContext, error: Error) | Called when transformation fails |

Usage:

import { QueryResultTransformerPlugin } from 'typeorm-query-hooks/plugins/query-result-transformer';

registerPlugin(QueryResultTransformerPlugin({
  transformers: {
    User: (user) => ({
      id: user.id,
      fullName: `${user.firstName} ${user.lastName}`,
      email: user.email,
      // Remove sensitive data
      password: undefined,
      resetToken: undefined
    }),
    Product: (product) => ({
      ...product,
      price: `$${product.price.toFixed(2)}`,
      inStock: product.quantity > 0
    })
  },
  enableLogging: true
}));

πŸ› οΈ Utilities

🏷️ TableExtractor - Extract table names from queries

What it does: Extracts all table names from any TypeORM query (SELECT, INSERT, UPDATE, DELETE, CTEs, subqueries, joins).

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | warnOnEmptyTables | boolean | false | Emit warning when table extraction returns empty array. Useful for debugging extraction logic or catching malformed queries. Usually safe to keep false in production. | | enableLogging | boolean | false | Log all extracted table names for every query. Verbose - use for debugging table extraction issues. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onTablesExtracted | (context: QueryHookContext, tables: string[]) | Called when tables are successfully extracted | | onEmptyTables | (context: QueryHookContext, queryType: string) | Called when no tables were extracted | | onWarning | (context: QueryHookContext, message: string) | Called for warnings | | onError | (context: QueryHookContext, error: Error) | Called when extraction fails |

Usage:

import { createTableExtractorPlugin, extractTablesFromBuilder } from 'typeorm-query-hooks/plugins/table-extractor';

registerPlugin(createTableExtractorPlugin({
  warnOnEmptyTables: true,
  enableLogging: true
}));

// Use directly in code:
const query = userRepo.createQueryBuilder('user')
  .leftJoin('user.posts', 'posts');
const tables = extractTablesFromBuilder(query);
// Or:
const tables2 = query.getInvolvedTables();

πŸ”€ QueryTypeDetector - Detect SQL query types

What it does: Detects the type of SQL query being executed (SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, etc.) using TypeORM's internal expressionMap (preferred) or SQL string parsing (fallback).

Why use expressionMap instead of SQL parsing?

  • βœ… More reliable - Uses TypeORM's internal state, not fragile regex
  • βœ… Faster - Direct property access vs string parsing
  • βœ… Type-safe - TypeORM guarantees the type values

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | monitorTypes | QueryType[] | [] (all) | Only monitor specific query types. Example: ['INSERT', 'UPDATE', 'DELETE'] for write operations only. Empty = monitor all types. | | enableLogging | boolean | false | Log all detected query types. Useful for debugging and analytics. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onQueryType | (context: QueryTypeContext) | Called for every query (filtered by monitorTypes) | | onSelect | (context: QueryTypeContext) | Called specifically for SELECT queries | | onInsert | (context: QueryTypeContext) | Called specifically for INSERT queries | | onUpdate | (context: QueryTypeContext) | Called specifically for UPDATE queries | | onDelete | (context: QueryTypeContext) | Called specifically for DELETE queries | | onDDL | (context: QueryTypeContext) | Called for DDL operations (CREATE, ALTER, DROP, TRUNCATE) | | onTransaction | (context: QueryTypeContext) | Called for transaction control (BEGIN, COMMIT, ROLLBACK) |

Supported Query Types:

  • DML: SELECT, INSERT, UPDATE, DELETE
  • DDL: CREATE, ALTER, DROP, TRUNCATE
  • Transaction: BEGIN, COMMIT, ROLLBACK
  • Other: WITH (CTEs), OTHER (unknown/unsupported)

Usage Examples:

import { 
  registerPlugin, 
  QueryTypeDetectorPlugin,
  extractQueryTypeFromBuilder,
  extractQueryTypeFromSQL
} from 'typeorm-query-hooks';

// Example 1: Monitor all write operations
registerPlugin(QueryTypeDetectorPlugin({
  monitorTypes: ['INSERT', 'UPDATE', 'DELETE'],
  onQueryType: (context) => {
    console.log(`Write operation: ${context.queryType} on ${context.sql.substring(0, 100)}`);
    // Track in analytics, metrics, etc.
  }
}));

// Example 2: Specific callbacks for each operation type
registerPlugin(QueryTypeDetectorPlugin({
  onSelect: (context) => {
    console.log(`Read query executed: ${context.executionTime}ms`);
  },
  onInsert: (context) => {
    console.log(`New data inserted`);
    // Invalidate cache, send notifications, etc.
  },
  onUpdate: (context) => {
    console.log(`Data updated`);
  },
  onDelete: (context) => {
    console.warn(`Data deleted - audit this!`);
  }
}));

// Example 3: Monitor DDL operations in production (security)
registerPlugin(QueryTypeDetectorPlugin({
  onDDL: (context) => {
    console.error(`⚠️ DDL operation in production: ${context.queryType}`);
    // Alert DevOps, log to security system
  }
}));

// Example 4: Use directly in code (like TableExtractor)
const repo = dataSource.getRepository(User);

// Detect from QueryBuilder (uses expressionMap - preferred!)
const selectQuery = repo.createQueryBuilder('user').where('user.id = :id', { id: 1 });
const type1 = extractQueryTypeFromBuilder(selectQuery); // "SELECT"

const insertQuery = repo.createQueryBuilder().insert().into(User).values({ name: 'John' });
const type2 = extractQueryTypeFromBuilder(insertQuery); // "INSERT"

// Detect from SQL string (fallback method)
const type3 = extractQueryTypeFromSQL('SELECT * FROM users'); // "SELECT"
const type4 = extractQueryTypeFromSQL('CREATE INDEX idx_name ON users(name)'); // "CREATE"

// Helper functions
import { isDML, isDDL, isTransaction } from 'typeorm-query-hooks';

console.log(isDML('SELECT'));      // true
console.log(isDML('INSERT'));      // true
console.log(isDML('CREATE'));      // false

console.log(isDDL('CREATE'));      // true
console.log(isDDL('ALTER'));       // true
console.log(isDDL('SELECT'));      // false

console.log(isTransaction('BEGIN'));   // true
console.log(isTransaction('COMMIT'));  // true
console.log(isTransaction('SELECT'));  // false

Real-World Use Cases:

// 1. Analytics: Track read vs write ratio
let readCount = 0, writeCount = 0;
registerPlugin(QueryTypeDetectorPlugin({
  onSelect: () => readCount++,
  onQueryType: (ctx) => {
    if (['INSERT', 'UPDATE', 'DELETE'].includes(ctx.queryType)) {
      writeCount++;
    }
  }
}));

// 2. Security: Alert on DDL in production
if (process.env.NODE_ENV === 'production') {
  registerPlugin(QueryTypeDetectorPlugin({
    onDDL: (context) => {
      alert.critical(`DDL operation detected: ${context.queryType}`, {
        sql: context.sql,
        timestamp: context.timestamp
      });
    }
  }));
}

// 3. Cache invalidation based on operation type
registerPlugin(QueryTypeDetectorPlugin({
  monitorTypes: ['INSERT', 'UPDATE', 'DELETE'],
  onQueryType: (context) => {
    // Extract tables and invalidate cache
    const tables = extractTablesFromBuilder(context.builder);
    tables.forEach(table => cache.invalidate(table));
  }
}));

// 4. Performance monitoring by query type
const metrics = {
  SELECT: { count: 0, totalTime: 0 },
  INSERT: { count: 0, totalTime: 0 },
  UPDATE: { count: 0, totalTime: 0 },
  DELETE: { count: 0, totalTime: 0 }
};

registerPlugin(QueryTypeDetectorPlugin({
  onQueryType: (context) => {
    const type = context.queryType;
    if (metrics[type]) {
      metrics[type].count++;
      metrics[type].totalTime += context.executionTime || 0;
    }
  }
}));

// Log metrics every 5 minutes
setInterval(() => {
  Object.entries(metrics).forEach(([type, data]) => {
    console.log(`${type}: ${data.count} queries, avg ${data.totalTime / data.count}ms`);
  });
}, 5 * 60 * 1000);

Comparison: expressionMap vs SQL Parsing

| Method | Speed | Accuracy | TypeORM Only | Raw SQL | |--------|-------|----------|--------------|---------| | extractQueryTypeFromBuilder (expressionMap) | ⚑ Fast | βœ… 100% | βœ… Yes | ❌ No | | extractQueryTypeFromSQL (string parsing) | 🐌 Slower | ⚠️ ~95% | βœ… Yes | βœ… Yes |

The plugin automatically uses the best method available:

  1. Priority 1: expressionMap from QueryBuilder (if available)
  2. Priority 2: SQL string parsing (fallback)

This ensures maximum reliability while supporting all query types!

Using in Your Logger:

Just like getTablesFromSQL(), you can use getExtendedQueryTypeFromSQL() to retrieve the query type in your custom logger:

import { getExtendedQueryTypeFromSQL, getTablesFromSQL } from 'typeorm-query-hooks';

class MyCustomLogger implements Logger {
  logQuery(query: string, parameters?: any[]): void {
    // Get query type extracted from expressionMap (if available)
    const queryType = getExtendedQueryTypeFromSQL(query);
    
    // Get tables extracted from expressionMap (if available)
    const tables = getTablesFromSQL(query);
    
    this.logger.info('Query executed', {
      query,
      queryType,  // "SELECT", "INSERT", "CREATE", "BEGIN", etc.
      tables: tables.join(', '),  // "users, posts"
      parameters
    });
  }
}

How it works:

  1. QueryTypeDetector plugin extracts query type from expressionMap during query execution
  2. Stores it in the metadata registry with AsyncLocalStorage for concurrent safety
  3. Your logger calls getExtendedQueryTypeFromSQL(sql) to retrieve it
  4. Returns the stored value instantly (no parsing needed!)

Benefits:

  • βœ… Accurate: Uses TypeORM's internal state, not regex
  • βœ… Fast: No string parsing, just registry lookup
  • βœ… Complete: Includes DDL, transactions, CTEs that SQL parsing might miss
  • βœ… Safe: Concurrent queries won't interfere with each other

βœ… ResultValidator - Validate query results

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | largeResultThreshold | number | 1000 | Number of rows to consider a result set "large". Triggers onLargeResult callback to suggest pagination. Adjust based on your app: lower for API endpoints (100-500), higher for reports (5000+). | | monitorTables | string[] | [] (all) | Limit validation to specific tables. Example: ['users', 'orders'] only checks these tables. Empty = monitor all. Use to focus on tables that should always be paginated. | | enableLogging | boolean | false | Log validation warnings (empty/large results) to console. |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onEmptyResult | (context: QueryResultContext) | Called when query returns no results | | onLargeResult | (context: QueryResultContext, rowCount: number) | Called when result exceeds largeResultThreshold |

Usage:

import { ResultValidatorPlugin } from 'typeorm-query-hooks/plugins/result-validator';

registerPlugin(ResultValidatorPlugin({
  largeResultThreshold: 5000,
  monitorTables: ['users', 'orders'],
  enableLogging: true,
  onLargeResult: (context) => {
    logger.warn(`Large result: ${context.rowCount} rows - consider pagination`);
  }
}));

✏️ QueryModifier - Modify queries before execution

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | enableLogging | boolean | false | Auto-log modifications |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | modifySql | (sql: string, context: PreQueryContext) => string | Modify SQL before execution | | modifyParameters | (parameters: any[], context: PreQueryContext) => any[] | Modify parameters before execution | | shouldExecute | (context: PreQueryContext) => boolean | Return false to cancel query | | onSqlModified | (context: PreQueryContext, originalSql: string, modifiedSql: string) | Called when SQL was modified | | onParametersModified | (context: PreQueryContext, originalParams: any[], modifiedParams: any[]) | Called when parameters were modified | | onError | (context: PreQueryContext, error: Error) | Called when modification fails |

Usage:

import { QueryModifierPlugin, TenantFilterModifier, SafetyModifier } from 'typeorm-query-hooks/plugins/query-modifier';

// Multi-tenancy
registerPlugin(TenantFilterModifier({
  getTenantId: () => getCurrentUser().tenantId,
  tables: ['orders', 'products'],
  tenantColumn: 'tenant_id'
}));

// Block queries during maintenance
registerPlugin(QueryModifierPlugin({
  shouldExecute: (context) => {
    if (isMaintenanceMode()) {
      console.error('Database in maintenance');
      return false;
    }
    return true;
  }
}));

πŸ” QueryComplexity - Warn on complex queries

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | maxJoins | number | 5 | Maximum number of JOIN clauses before flagging as complex. Queries with many joins are often slow and hard to maintain. Consider denormalization or caching for queries exceeding this. | | maxTables | number | 10 | Maximum number of tables referenced before flagging as complex. High table count suggests overly complex query that may need refactoring. | | warnOnSubqueries | boolean | false | Flag queries containing subqueries. Subqueries can be slow (especially correlated ones). Set to true to identify candidates for optimization with JOINs or separate queries. | | warnOnCTEs | boolean | false | Flag queries with Common Table Expressions (WITH clauses). CTEs can impact performance in some databases. Enable to audit CTE usage. | | enableLogging | boolean | false | Log complexity warnings to console with metrics (join count, table count, etc.). |

Event Callbacks:

| Callback | Parameters | Description | |----------|------------|-------------| | onComplexQuery | (context: PreQueryContext, complexity: QueryComplexityInfo) | Called when query exceeds complexity thresholds |

Usage:

import { QueryComplexityPlugin } from 'typeorm-query-hooks/plugins/query-complexity';

registerPlugin(QueryComplexityPlugin({
  maxJoins: 3,
  maxTables: 5,
  warnOnSubqueries: true,
  enableLogging: true
}));

πŸͺ΅ QueryLogger - Custom query logging with filters

See plugin documentation for details.

πŸ’Ύ QueryMetadataRegistry - Store query metadata

Automatically registered when using NestJS integration. See NestJS section below.


🎨 Creating Custom Plugins

import { QueryHookPlugin } from 'typeorm-query-hooks';

const MyCustomPlugin: QueryHookPlugin = {
  name: 'MyCustomPlugin',
  
  onQueryBuild: (context) => {
    console.log('Query built:', context.sql);
  },
  
  onQueryComplete: (context) => {
    console.log(`Query took ${context.executionTime}ms`);
  },
  
  onSlowQuery: (context) => {
    console.warn('Slow query detected!');
  }
};

registerPlugin(MyCustomPlugin);

πŸ”§ NestJS Integration

// main.ts or app.module.ts
import { enableQueryHooks, registerPlugin } from 'typeorm-query-hooks';
import { NPlusOneDetectorPlugin } from 'typeorm-query-hooks/plugins/n-plus-one-detector';
import { SafetyGuardPlugin } from 'typeorm-query-hooks/plugins/safety-guard';

// Enable before TypeORM connection
enableQueryHooks({ verbose: false });

// Register essential plugins
registerPlugin(NPlusOneDetectorPlugin({ threshold: 5, enableLogging: true }));
registerPlugin(SafetyGuardPlugin({ requireWhereClause: true }));

@Module({
  imports: [TypeOrmModule.forRoot({ /* ... */ })],
})
export class AppModule {}

πŸ› Debugging

Enable verbose mode to see detailed hook execution:

enableQueryHooks({ verbose: true });

What it logs:

  • When hooks are enabled
  • When plugins are registered
  • When each hook fires
  • SQL queries being captured
  • Tables extracted from queries
  • Execution timing

πŸ“Š Real-World Impact

Before typeorm-query-hooks:

  • ❌ N+1 queries slow down production (found after users complain)
  • ❌ Accidental DELETE FROM users without WHERE
  • ❌ Connection pool exhausted β†’ app crash
  • ❌ Slow query β†’ manually copy SQL β†’ run EXPLAIN β†’ find issue
  • ❌ Don't know which file:line caused the query

After typeorm-query-hooks:

  • βœ… N+1 detected in development automatically
  • βœ… Dangerous queries blocked before execution
  • βœ… Connection leaks caught immediately
  • βœ… EXPLAIN runs automatically on slow queries
  • βœ… Exact source location shown for every query

🌟 Show Your Support

If this library helps you, please give it a ⭐️ on GitHub!


πŸ“˜ TypeScript Types Reference

All callback parameter types are fully exported and available for import:

Core Context Types

import {
  QueryHookContext,           // Base context for all hooks
  PreQueryContext,            // Pre-execution hook with setSql(), setParameters()
  QueryExecutionContext,      // Post-execution with executionTime
  QueryResultContext,         // With result, rowCount, isEmpty
  TransactionContext,         // Transaction lifecycle events
  ConnectionPoolContext,      // Connection pool metrics
} from 'typeorm-query-hooks';

Plugin-Specific Types

// Connection Leak Detector
import { ConnectionLeak } from 'typeorm-query-hooks';

// Safety Guard
import { BlockedOperation } from 'typeorm-query-hooks';

// Audit Logging
import { AuditLogEntry } from 'typeorm-query-hooks';

// Query Source Tracer
import { SourceLocation, QueryContextWithSource } from 'typeorm-query-hooks';

// Slow Query Analyzer
import { QueryExecutionPlan } from 'typeorm-query-hooks';

// Query Complexity
import { QueryComplexityMetrics } from 'typeorm-query-hooks';

// Idle Transaction Monitor
import { ZombieTransaction } from 'typeorm-query-hooks';

// Query Result Transformer
import { TransformerFn } from 'typeorm-query-hooks';

Usage Example with Types

import { 
  registerPlugin,
  QueryExecutionContext,
  ConnectionLeak,
  BlockedOperation 
} from 'typeorm-query-hooks';
import { SafetyGuardPlugin } from 'typeorm-query-hooks/plugins/safety-guard';
import { ConnectionLeakDetectorPlugin } from 'typeorm-query-hooks/plugins/connection-leak-detector';

// Type-safe callbacks
registerPlugin(SafetyGuardPlugin({
  onBlocked: (context: PreQueryContext, blocked: BlockedOperation) => {
    logger.error('Blocked operation', {
      operation: blocked.operation,
      tables: blocked.tables,
      sql: context.sql
    });
  }
}));

registerPlugin(ConnectionLeakDetectorPlugin({
  onLeak: (leak: ConnectionLeak) => {
    logger.error('Connection leak detected', {
      age: leak.age,
      stackTrace: leak.stackTrace
    });
  }
}));

Note: All types are generated from TypeScript source and include full JSDoc comments for IDE autocomplete.


πŸ“„ License

MIT Β© Roy Leibovitz


Built with ❀️ to make TypeORM better for everyone