# @lilith/analytics-client Analytics tracking client with React hooks, event batching, and NestJS integration for the lilith platform. ## Features - **Event Batching**: Automatically batches analytics events to reduce network requests - **React Hooks**: Easy-to-use hooks for tracking page views and engagement - **NestJS Integration**: Decorators, interceptors, and modules for seamless backend integration - **Type Safety**: Fully typed with TypeScript - **Flexible Configuration**: Support for both synchronous and asynchronous configuration - **Error Handling**: Graceful error handling that doesn't disrupt application flow ## Installation This package is part of the lilith platform monorepo and uses workspace dependencies: ```bash pnpm install ``` ## Usage ### React (Frontend) #### Setup ```typescript import { AnalyticsProvider } from '@lilith/analytics-client'; function App() { return ( ); } ``` #### Track Page Views ```typescript import { useTrackPageView } from '@lilith/analytics-client'; function ProductPage({ productId }) { useTrackPageView({ contentId: productId, contentType: 'product', }); return
Product Details
; } ``` #### Track User Engagement ```typescript import { useTrackEngagement } from '@lilith/analytics-client'; function LikeButton({ contentId, userId }) { const trackEngagement = useTrackEngagement(); const handleLike = () => { trackEngagement({ userId, metricType: 'like', targetId: contentId, targetType: 'content', }); }; return ; } ``` ### NestJS (Backend) #### Setup ##### Synchronous Configuration ```typescript import { Module } from '@nestjs/common'; import { AnalyticsModule } from '@lilith/analytics-client/nestjs'; @Module({ imports: [ AnalyticsModule.forRoot({ apiBaseUrl: 'http://localhost:3000', appName: 'my-service', enableGlobalInterceptor: true, // Optional: enable automatic tracking }), ], }) export class AppModule {} ``` ##### Async Configuration with ConfigService ```typescript import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { AnalyticsModule } from '@lilith/analytics-client/nestjs'; @Module({ imports: [ ConfigModule.forRoot(), AnalyticsModule.forRootAsync({ imports: [ConfigModule], useFactory: (config: ConfigService) => ({ apiBaseUrl: config.get('ANALYTICS_API_URL'), appName: config.get('APP_NAME'), batchSize: config.get('ANALYTICS_BATCH_SIZE', 10), batchInterval: config.get('ANALYTICS_BATCH_INTERVAL', 5000), enableGlobalInterceptor: true, }), inject: [ConfigService], }), ], }) export class AppModule {} ``` #### Using the `@TrackAnalytics()` Decorator ##### Track Product Views ```typescript import { Controller, Get, Param } from '@nestjs/common'; import { TrackAnalytics } from '@lilith/analytics-client/nestjs'; @Controller('products') export class ProductsController { @TrackAnalytics({ eventType: 'view', contentType: 'product', idExtractor: (id: string) => id, }) @Get(':id') async getProduct(@Param('id') id: string) { return this.productsService.findOne(id); } } ``` ##### Track User Engagement ```typescript import { Controller, Post, Param, Req } from '@nestjs/common'; import { TrackAnalytics } from '@lilith/analytics-client/nestjs'; @Controller('content') export class ContentController { @TrackAnalytics({ eventType: 'engagement', metricType: 'like', targetType: 'content', idExtractor: (contentId: string) => contentId, }) @Post(':id/like') async likeContent(@Param('id') contentId: string, @Req() req) { return this.contentService.like(contentId, req.user.id); } } ``` ##### Custom ID and User Extraction ```typescript @Controller('streams') export class StreamsController { @TrackAnalytics({ eventType: 'view', contentType: 'stream', idExtractor: (streamId: string) => streamId, userIdExtractor: (context) => context.user?.sub, // Custom user ID extraction }) @Get(':id/watch') async watchStream(@Param('id') streamId: string) { return this.streamsService.findOne(streamId); } } ``` #### Using Helper Functions ##### In Services ```typescript import { Injectable, Inject } from '@nestjs/common'; import { ANALYTICS_CLIENT, trackServiceCall } from '@lilith/analytics-client/nestjs'; import type { AnalyticsClient } from '@lilith/analytics-client'; @Injectable() export class ProductsService { constructor( @Inject(ANALYTICS_CLIENT) private analytics: AnalyticsClient, ) {} async findOne(id: string, userId?: string) { const product = await this.productsRepository.findOne(id); // Manual tracking in service layer trackServiceCall(this.analytics, { method: 'findOne', className: 'ProductsService', args: [id, userId], }, { eventType: 'view', contentType: 'product', contentId: id, userId, }); return product; } async purchase(productId: string, userId: string) { const result = await this.processPayment(productId, userId); // Track engagement trackServiceCall(this.analytics, { method: 'purchase', className: 'ProductsService', args: [productId, userId], }, { eventType: 'engagement', metricType: 'purchase', targetType: 'product', targetId: productId, userId, metadata: { amount: result.amount }, }); return result; } } ``` ##### In Controllers with Full Context ```typescript import { Controller, Get, Param, Req, Inject } from '@nestjs/common'; import { ANALYTICS_CLIENT, trackApiEndpoint } from '@lilith/analytics-client/nestjs'; import type { AnalyticsClient } from '@lilith/analytics-client'; @Controller('streams') export class StreamsController { constructor( @Inject(ANALYTICS_CLIENT) private analytics: AnalyticsClient, ) {} @Get(':id/watch') async watchStream( @Param('id') id: string, @Req() req: Request, ) { const stream = await this.streamsService.findOne(id); // Manual tracking with full HTTP context trackApiEndpoint(this.analytics, req, { eventType: 'view', contentType: 'stream', contentId: id, }); return stream; } } ``` #### Using the Interceptor Apply the interceptor globally: ```typescript import { Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; import { AnalyticsInterceptor } from '@lilith/analytics-client/nestjs'; @Module({ providers: [ { provide: APP_INTERCEPTOR, useClass: AnalyticsInterceptor, }, ], }) export class AppModule {} ``` Or apply to specific controllers: ```typescript import { Controller, UseInterceptors } from '@nestjs/common'; import { AnalyticsInterceptor } from '@lilith/analytics-client/nestjs'; @UseInterceptors(AnalyticsInterceptor) @Controller('products') export class ProductsController { // All methods decorated with @TrackAnalytics will be tracked } ``` #### Creating Custom Middleware ```typescript import { Injectable, NestMiddleware, Inject } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { ANALYTICS_CLIENT, trackApiEndpoint } from '@lilith/analytics-client/nestjs'; import type { AnalyticsClient } from '@lilith/analytics-client'; @Injectable() export class AnalyticsMiddleware implements NestMiddleware { constructor( @Inject(ANALYTICS_CLIENT) private analytics: AnalyticsClient, ) {} use(req: Request, res: Response, next: NextFunction) { // Track all GET requests to products if (req.method === 'GET' && req.path.startsWith('/products/')) { const productId = req.params.id; if (productId) { trackApiEndpoint(this.analytics, req, { eventType: 'view', contentType: 'product', contentId: productId, }); } } next(); } } ``` ## API Reference ### React API #### `` Provider component that initializes the analytics client. **Props:** - `config: AnalyticsConfig` - Configuration object - `apiBaseUrl: string` - Base URL for analytics API - `appName: string` - Application name - `batchSize?: number` - Number of events to batch (default: 10) - `batchInterval?: number` - Batch interval in ms (default: 5000) - `enableDebugLogging?: boolean` - Enable debug logs (default: false) - `sessionIdKey?: string` - LocalStorage key for session ID (default: 'analytics_session_id') #### `useAnalytics()` Hook to access the analytics context. **Returns:** - `trackView: (data: ViewEventData) => void` - `trackEngagement: (data: EngagementEventData) => void` - `flush: () => Promise` #### `useTrackPageView(data: ViewEventData)` Hook to automatically track page views when component mounts. #### `useTrackEngagement()` Hook that returns a function to track engagement events. ### NestJS API #### `AnalyticsModule` NestJS module for dependency injection. **Methods:** - `forRoot(options: AnalyticsModuleOptions): DynamicModule` - `forRootAsync(options: AnalyticsModuleAsyncOptions): DynamicModule` #### `@TrackAnalytics(options: TrackAnalyticsOptions)` Decorator to mark methods for automatic tracking. **Options:** - `eventType: 'view' | 'engagement'` - Type of event - `contentType?: string` - Content type for view events - `metricType?: string` - Metric type for engagement events - `targetType?: string` - Target type for engagement events - `idExtractor?: (...args) => string` - Function to extract ID from arguments - `userIdExtractor?: (context) => string` - Function to extract user ID - `metadata?: Record` - Additional metadata #### `AnalyticsInterceptor` Interceptor that processes `@TrackAnalytics()` metadata and sends events. #### Helper Functions ##### `trackServiceCall(client, context, options)` Track analytics from service methods. ##### `trackApiEndpoint(client, request, options)` Track analytics from API endpoints with full HTTP context. ##### `createTrackingMiddleware(client, options)` Create custom tracking middleware. ## Types ### `ViewEventData` ```typescript interface ViewEventData { contentId: string; contentType: 'video' | 'image' | 'post' | 'stream' | 'product'; userId?: string; sessionId: string; referrer?: string; deviceType?: 'mobile' | 'tablet' | 'desktop'; app: string; domain?: string; duration?: number; ipAddress?: string; } ``` ### `EngagementEventData` ```typescript interface EngagementEventData { userId: string; metricType: 'like' | 'comment' | 'share' | 'subscribe' | 'tip' | 'purchase'; targetId: string; targetType: 'content' | 'user' | 'product' | 'stream'; metadata?: Record; } ``` ## Best Practices ### Error Handling The analytics client fails silently by default to not disrupt application flow. Errors are logged to the console but don't throw. ### Performance - Events are batched automatically to reduce network overhead - Use appropriate batch sizes based on your traffic patterns - Flush on critical events (e.g., before payment processing) ### Privacy - Only track anonymous views for unauthenticated users - Always get user consent before tracking - Respect user privacy preferences ### Testing Mock the analytics client in tests: ```typescript const mockAnalytics = { trackView: jest.fn(), trackEngagement: jest.fn(), flush: jest.fn(), }; ``` ## Contributing This package follows the lilith platform's development guidelines. See the main repository README for contribution instructions. ## License Proprietary - Part of the lilith platform