No description
|
Some checks failed
Build and Publish / build-and-publish (push) Failing after 41s
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| .githooks | ||
| src | ||
| .gitignore | ||
| eslint.config.js | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsup.config.ts | ||
@lilith/nestjs-analytics-interceptor
NestJS interceptor for tracking API calls with configurable analytics backends.
Features
- Request Tracking: Capture endpoint, method, duration, status code
- Client IP Detection: Extract real client IP from proxied requests
- Configurable Exclusions: Skip tracking for specific endpoints
- Async Configuration: Support for dynamic module configuration
- Debug Logging: Optional verbose logging for development
Installation
pnpm add @lilith/nestjs-analytics-interceptor
Peer Dependencies
pnpm add @nestjs/common @nestjs/core @nestjs/platform-express rxjs
Quick Start
import { Module } from '@nestjs/common';
import { AnalyticsModule } from '@lilith/nestjs-analytics-interceptor';
@Module({
imports: [
AnalyticsModule.register({
apiBaseUrl: 'https://analytics.example.com',
appName: 'my-api',
enableDebugLogging: true,
excludeEndpoints: ['/health', '/metrics'],
}),
],
})
export class AppModule {}
Configuration
Sync Configuration
AnalyticsModule.register({
apiBaseUrl: 'https://analytics.example.com',
appName: 'my-api',
enableDebugLogging: false,
customHeaders: {
'X-Analytics-Key': 'secret-key',
},
excludeEndpoints: ['/health', '/health/*', '/metrics'],
});
Async Configuration
AnalyticsModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
apiBaseUrl: config.get('ANALYTICS_URL'),
appName: config.get('APP_NAME'),
enableDebugLogging: config.get('DEBUG') === 'true',
}),
});
Using a Factory Class
@Injectable()
class AnalyticsOptionsFactory implements AnalyticsModuleOptionsFactory {
constructor(private readonly config: ConfigService) {}
createAnalyticsOptions(): AnalyticsModuleOptions {
return {
apiBaseUrl: this.config.get('ANALYTICS_URL'),
appName: this.config.get('APP_NAME'),
};
}
}
AnalyticsModule.registerAsync({
useClass: AnalyticsOptionsFactory,
});
API Reference
AnalyticsModuleOptions
interface AnalyticsModuleOptions {
/** Base URL for the analytics API */
apiBaseUrl: string;
/** Application name for identifying the source */
appName: string;
/** Enable debug logging (default: false) */
enableDebugLogging?: boolean;
/** Custom headers for analytics requests */
customHeaders?: Record<string, string>;
/** Endpoints to exclude (supports glob patterns) */
excludeEndpoints?: string[];
}
AnalyticsEvent
Events tracked by the interceptor:
interface AnalyticsEvent {
endpoint: string; // Request path
method: string; // HTTP method
userId?: string; // User ID if available
statusCode: number; // Response status code
duration: number; // Request duration in ms
ipAddress: string; // Client IP address
userAgent?: string; // User-Agent header
timestamp: Date; // Event timestamp
appName: string; // Configured app name
metadata?: Record<string, unknown>;
}
Utilities
getClientIp(request)
Extract the real client IP from a request, handling proxied requests:
import { getClientIp } from '@lilith/nestjs-analytics-interceptor';
const ip = getClientIp(request);
// Checks: X-Forwarded-For, X-Real-IP, CF-Connecting-IP, socket.remoteAddress
normalizeIp(ip)
Normalize IPv6-mapped IPv4 addresses:
import { normalizeIp } from '@lilith/nestjs-analytics-interceptor';
normalizeIp('::ffff:192.168.1.1'); // '192.168.1.1'
normalizeIp('192.168.1.1'); // '192.168.1.1'
Using the Interceptor Directly
For custom interceptor usage:
import { Controller, UseInterceptors } from '@nestjs/common';
import { AnalyticsInterceptor } from '@lilith/nestjs-analytics-interceptor';
@Controller('api')
@UseInterceptors(AnalyticsInterceptor)
export class ApiController {
// ...
}
License
MIT