# @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