# Basic Usage Examples

This document provides practical examples of using `@nestjsvn/swagger-sse` in real-world scenarios.

## Table of Contents

1. [Simple Event Stream](#simple-event-stream)
2. [Multi-Event Type Stream](#multi-event-type-stream)
3. [Authenticated Streams](#authenticated-streams)
4. [Error Handling](#error-handling)
5. [Query Parameter Filtering](#query-parameter-filtering)
6. [Real-time Chat Application](#real-time-chat-application)
7. [System Monitoring Dashboard](#system-monitoring-dashboard)
8. [E-commerce Order Tracking](#e-commerce-order-tracking)

## Simple Event Stream

Basic SSE endpoint that emits periodic events:

```typescript
// dto/notification.dto.ts
export class NotificationDto {
  id: string;
  title: string;
  message: string;
  type: 'info' | 'warning' | 'error' | 'success';
  timestamp: Date;
}

// controllers/notifications.controller.ts
import { Controller, Sse } from '@nestjs/common';
import { Observable, interval, map } from 'rxjs';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { NotificationDto } from '../dto/notification.dto';

@Controller('notifications')
export class NotificationsController {
  @Sse('stream')
  @ApiSse({
    summary: 'Notification stream',
    description: 'Real-time notifications for the application',
    events: {
      'notification': NotificationDto,
    },
  })
  streamNotifications(): Observable<MessageEvent> {
    return interval(3000).pipe(
      map((i) => ({
        data: JSON.stringify({
          id: `notif-${i}`,
          title: `Notification ${i}`,
          message: `This is notification number ${i}`,
          type: i % 4 === 0 ? 'error' : i % 3 === 0 ? 'warning' : i % 2 === 0 ? 'success' : 'info',
          timestamp: new Date(),
        }),
        type: 'notification',
      } as MessageEvent))
    );
  }
}
```

## Multi-Event Type Stream

Stream that emits different types of events:

```typescript
// dto/user-events.dto.ts
export class UserCreatedDto {
  userId: string;
  username: string;
  email: string;
  createdAt: Date;
}

export class UserUpdatedDto {
  userId: string;
  username: string;
  email: string;
  updatedAt: Date;
  changes: string[];
}

export class UserDeletedDto {
  userId: string;
  deletedAt: Date;
  reason?: string;
}

// controllers/user-events.controller.ts
import { Controller, Sse } from '@nestjs/common';
import { Observable, merge, interval, map, switchMap, of } from 'rxjs';
import { ApiSse } from '@nestjsvn/swagger-sse';

@Controller('user-events')
export class UserEventsController {
  @Sse('stream')
  @ApiSse({
    summary: 'User lifecycle events',
    description: 'Stream of user creation, update, and deletion events',
    events: {
      'user-created': UserCreatedDto,
      'user-updated': UserUpdatedDto,
      'user-deleted': UserDeletedDto,
    },
  })
  streamUserEvents(): Observable<MessageEvent> {
    // Simulate different event types
    const createEvents = interval(5000).pipe(
      map((i) => ({
        data: JSON.stringify({
          userId: `user-${i}`,
          username: `user${i}`,
          email: `user${i}@example.com`,
          createdAt: new Date(),
        }),
        type: 'user-created',
      } as MessageEvent))
    );

    const updateEvents = interval(7000).pipe(
      map((i) => ({
        data: JSON.stringify({
          userId: `user-${i}`,
          username: `updated-user${i}`,
          email: `updated-user${i}@example.com`,
          updatedAt: new Date(),
          changes: ['username', 'email'],
        }),
        type: 'user-updated',
      } as MessageEvent))
    );

    const deleteEvents = interval(15000).pipe(
      map((i) => ({
        data: JSON.stringify({
          userId: `user-${i}`,
          deletedAt: new Date(),
          reason: 'Account deactivated',
        }),
        type: 'user-deleted',
      } as MessageEvent))
    );

    return merge(createEvents, updateEvents, deleteEvents);
  }
}
```

## Authenticated Streams

SSE endpoint that requires authentication:

```typescript
// guards/jwt-auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    
    if (!token) {
      return false;
    }

    try {
      const payload = this.jwtService.verify(token);
      request.user = payload;
      return true;
    } catch {
      return false;
    }
  }

  private extractTokenFromHeader(request: any): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

// dto/private-event.dto.ts
export class PrivateEventDto {
  eventId: string;
  userId: string;
  eventType: string;
  data: any;
  timestamp: Date;
}

// controllers/private-events.controller.ts
import { Controller, Sse, UseGuards, Request } from '@nestjs/common';
import { Observable, interval, map, filter } from 'rxjs';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { PrivateEventDto } from '../dto/private-event.dto';

@Controller('private-events')
@ApiTags('Private Events')
export class PrivateEventsController {
  @Sse('user-specific')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiSse({
    summary: 'User-specific event stream',
    description: 'Private events stream for authenticated users',
    security: [{ bearer: [] }],
    events: {
      'private-event': PrivateEventDto,
    },
  })
  streamPrivateEvents(@Request() req): Observable<MessageEvent> {
    const userId = req.user.sub;
    
    return interval(2000).pipe(
      map((i) => ({
        data: JSON.stringify({
          eventId: `event-${userId}-${i}`,
          userId: userId,
          eventType: 'user-activity',
          data: { action: 'page-view', page: `/dashboard/${i}` },
          timestamp: new Date(),
        }),
        type: 'private-event',
      } as MessageEvent))
    );
  }
}
```

## Error Handling

Stream with comprehensive error handling:

```typescript
// dto/error-event.dto.ts
export class ErrorEventDto {
  errorId: string;
  message: string;
  code: number;
  timestamp: Date;
  context?: any;
}

export class DataEventDto {
  id: string;
  data: any;
  timestamp: Date;
}

// services/data.service.ts
import { Injectable } from '@nestjs/common';
import { Observable, throwError, of, interval } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

@Injectable()
export class DataService {
  getData(): Observable<any> {
    return interval(1000).pipe(
      switchMap((i) => {
        // Simulate random errors
        if (i > 0 && i % 10 === 0) {
          return throwError(() => new Error(`Simulated error at interval ${i}`));
        }
        return of({ value: i, timestamp: new Date() });
      })
    );
  }
}

// controllers/data-stream.controller.ts
import { Controller, Sse } from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { map, catchError, startWith } from 'rxjs/operators';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { DataService } from '../services/data.service';

@Controller('data-stream')
export class DataStreamController {
  constructor(private dataService: DataService) {}

  @Sse('with-errors')
  @ApiSse({
    summary: 'Data stream with error handling',
    description: 'Stream that demonstrates error handling patterns',
    events: {
      'data-event': DataEventDto,
      'error-event': ErrorEventDto,
    },
  })
  streamWithErrorHandling(): Observable<MessageEvent> {
    return this.dataService.getData().pipe(
      map((data) => ({
        data: JSON.stringify({
          id: `data-${Date.now()}`,
          data: data,
          timestamp: new Date(),
        }),
        type: 'data-event',
      } as MessageEvent)),
      catchError((error) => {
        console.error('Stream error:', error);
        return of({
          data: JSON.stringify({
            errorId: `error-${Date.now()}`,
            message: error.message,
            code: 500,
            timestamp: new Date(),
            context: { source: 'data-service' },
          }),
          type: 'error-event',
        } as MessageEvent);
      }),
      startWith({
        data: JSON.stringify({
          id: 'stream-start',
          data: { message: 'Stream started' },
          timestamp: new Date(),
        }),
        type: 'data-event',
      } as MessageEvent)
    );
  }
}
```

## Query Parameter Filtering

Stream that supports filtering via query parameters:

```typescript
// controllers/filtered-events.controller.ts
import { Controller, Sse, Query } from '@nestjs/common';
import { Observable, interval, map, filter } from 'rxjs';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { ApiQuery } from '@nestjs/swagger';

@Controller('filtered-events')
export class FilteredEventsController {
  @Sse('stream')
  @ApiQuery({ name: 'category', required: false, description: 'Filter events by category' })
  @ApiQuery({ name: 'priority', required: false, description: 'Filter events by priority level' })
  @ApiSse({
    summary: 'Filtered event stream',
    description: 'Stream events filtered by query parameters',
    events: {
      'filtered-event': {
        id: 'string',
        category: 'string',
        priority: 'number',
        message: 'string',
        timestamp: 'Date',
      },
    },
  })
  streamFilteredEvents(
    @Query('category') category?: string,
    @Query('priority') priority?: string,
  ): Observable<MessageEvent> {
    const priorityNum = priority ? parseInt(priority, 10) : undefined;
    
    return interval(1000).pipe(
      map((i) => {
        const eventCategory = ['system', 'user', 'security', 'performance'][i % 4];
        const eventPriority = (i % 5) + 1;
        
        return {
          id: `event-${i}`,
          category: eventCategory,
          priority: eventPriority,
          message: `Event ${i} in ${eventCategory} with priority ${eventPriority}`,
          timestamp: new Date(),
        };
      }),
      filter((event) => {
        if (category && event.category !== category) return false;
        if (priorityNum && event.priority !== priorityNum) return false;
        return true;
      }),
      map((event) => ({
        data: JSON.stringify(event),
        type: 'filtered-event',
      } as MessageEvent))
    );
  }
}
```

## Real-time Chat Application

Complete chat application with SSE:

```typescript
// dto/chat.dto.ts
export class ChatMessageDto {
  messageId: string;
  roomId: string;
  userId: string;
  username: string;
  message: string;
  timestamp: Date;
}

export class UserJoinedDto {
  userId: string;
  username: string;
  roomId: string;
  timestamp: Date;
}

export class UserLeftDto {
  userId: string;
  username: string;
  roomId: string;
  timestamp: Date;
}

// services/chat.service.ts
import { Injectable } from '@nestjs/common';
import { Subject, Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';

@Injectable()
export class ChatService {
  private eventSubject = new Subject<any>();

  sendMessage(roomId: string, userId: string, username: string, message: string) {
    this.eventSubject.next({
      type: 'chat-message',
      data: {
        messageId: `msg-${Date.now()}`,
        roomId,
        userId,
        username,
        message,
        timestamp: new Date(),
      },
    });
  }

  userJoined(roomId: string, userId: string, username: string) {
    this.eventSubject.next({
      type: 'user-joined',
      data: {
        userId,
        username,
        roomId,
        timestamp: new Date(),
      },
    });
  }

  userLeft(roomId: string, userId: string, username: string) {
    this.eventSubject.next({
      type: 'user-left',
      data: {
        userId,
        username,
        roomId,
        timestamp: new Date(),
      },
    });
  }

  getEventsForRoom(roomId: string): Observable<MessageEvent> {
    return this.eventSubject.pipe(
      filter((event) => event.data.roomId === roomId),
      map((event) => ({
        data: JSON.stringify(event.data),
        type: event.type,
      } as MessageEvent))
    );
  }
}

// controllers/chat.controller.ts
import { Controller, Sse, Query, Post, Body } from '@nestjs/common';
import { Observable } from 'rxjs';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { ApiQuery, ApiBody, ApiTags } from '@nestjs/swagger';
import { ChatService } from '../services/chat.service';

@Controller('chat')
@ApiTags('Chat')
export class ChatController {
  constructor(private chatService: ChatService) {}

  @Sse('room/:roomId')
  @ApiQuery({ name: 'roomId', description: 'Chat room ID' })
  @ApiSse({
    summary: 'Chat room event stream',
    description: 'Real-time chat events for a specific room',
    events: {
      'chat-message': ChatMessageDto,
      'user-joined': UserJoinedDto,
      'user-left': UserLeftDto,
    },
  })
  streamChatEvents(@Query('roomId') roomId: string): Observable<MessageEvent> {
    return this.chatService.getEventsForRoom(roomId);
  }

  @Post('send-message')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        roomId: { type: 'string' },
        userId: { type: 'string' },
        username: { type: 'string' },
        message: { type: 'string' },
      },
    },
  })
  sendMessage(@Body() body: any) {
    this.chatService.sendMessage(body.roomId, body.userId, body.username, body.message);
    return { success: true };
  }

  @Post('join-room')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        roomId: { type: 'string' },
        userId: { type: 'string' },
        username: { type: 'string' },
      },
    },
  })
  joinRoom(@Body() body: any) {
    this.chatService.userJoined(body.roomId, body.userId, body.username);
    return { success: true };
  }
}
```

## System Monitoring Dashboard

Real-time system metrics streaming:

```typescript
// dto/metrics.dto.ts
export class CpuMetricDto {
  timestamp: Date;
  usage: number;
  cores: number;
}

export class MemoryMetricDto {
  timestamp: Date;
  used: number;
  total: number;
  percentage: number;
}

export class NetworkMetricDto {
  timestamp: Date;
  bytesIn: number;
  bytesOut: number;
  packetsIn: number;
  packetsOut: number;
}

// services/metrics.service.ts
import { Injectable } from '@nestjs/common';
import { Observable, interval } from 'rxjs';
import { map } from 'rxjs/operators';
import * as os from 'os';

@Injectable()
export class MetricsService {
  getCpuMetrics(): Observable<CpuMetricDto> {
    return interval(1000).pipe(
      map(() => ({
        timestamp: new Date(),
        usage: Math.random() * 100,
        cores: os.cpus().length,
      }))
    );
  }

  getMemoryMetrics(): Observable<MemoryMetricDto> {
    return interval(2000).pipe(
      map(() => {
        const total = os.totalmem();
        const free = os.freemem();
        const used = total - free;
        return {
          timestamp: new Date(),
          used: used,
          total: total,
          percentage: (used / total) * 100,
        };
      })
    );
  }

  getNetworkMetrics(): Observable<NetworkMetricDto> {
    return interval(3000).pipe(
      map(() => ({
        timestamp: new Date(),
        bytesIn: Math.floor(Math.random() * 1000000),
        bytesOut: Math.floor(Math.random() * 1000000),
        packetsIn: Math.floor(Math.random() * 10000),
        packetsOut: Math.floor(Math.random() * 10000),
      }))
    );
  }
}

// controllers/metrics.controller.ts
import { Controller, Sse } from '@nestjs/common';
import { Observable, merge } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { ApiTags } from '@nestjs/swagger';
import { MetricsService } from '../services/metrics.service';

@Controller('metrics')
@ApiTags('System Metrics')
export class MetricsController {
  constructor(private metricsService: MetricsService) {}

  @Sse('system')
  @ApiSse({
    summary: 'System metrics stream',
    description: 'Real-time system performance metrics',
    events: {
      'cpu-metric': CpuMetricDto,
      'memory-metric': MemoryMetricDto,
      'network-metric': NetworkMetricDto,
    },
  })
  streamSystemMetrics(): Observable<MessageEvent> {
    const cpuEvents = this.metricsService.getCpuMetrics().pipe(
      map((data) => ({
        data: JSON.stringify(data),
        type: 'cpu-metric',
      } as MessageEvent))
    );

    const memoryEvents = this.metricsService.getMemoryMetrics().pipe(
      map((data) => ({
        data: JSON.stringify(data),
        type: 'memory-metric',
      } as MessageEvent))
    );

    const networkEvents = this.metricsService.getNetworkMetrics().pipe(
      map((data) => ({
        data: JSON.stringify(data),
        type: 'network-metric',
      } as MessageEvent))
    );

    return merge(cpuEvents, memoryEvents, networkEvents);
  }
}
```

## E-commerce Order Tracking

Order status updates via SSE:

```typescript
// dto/order-events.dto.ts
export class OrderCreatedDto {
  orderId: string;
  customerId: string;
  items: Array<{ productId: string; quantity: number; price: number }>;
  total: number;
  createdAt: Date;
}

export class OrderStatusUpdatedDto {
  orderId: string;
  customerId: string;
  status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';
  updatedAt: Date;
  estimatedDelivery?: Date;
}

export class PaymentProcessedDto {
  orderId: string;
  customerId: string;
  amount: number;
  paymentMethod: string;
  processedAt: Date;
}

// services/order.service.ts
import { Injectable } from '@nestjs/common';
import { Subject, Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';

@Injectable()
export class OrderService {
  private orderEvents = new Subject<any>();

  createOrder(customerId: string, items: any[], total: number) {
    const orderId = `order-${Date.now()}`;
    this.orderEvents.next({
      type: 'order-created',
      data: {
        orderId,
        customerId,
        items,
        total,
        createdAt: new Date(),
      },
    });
    return orderId;
  }

  updateOrderStatus(orderId: string, customerId: string, status: string) {
    this.orderEvents.next({
      type: 'order-status-updated',
      data: {
        orderId,
        customerId,
        status,
        updatedAt: new Date(),
        estimatedDelivery: status === 'shipped' ? new Date(Date.now() + 3 * 24 * 60 * 60 * 1000) : undefined,
      },
    });
  }

  processPayment(orderId: string, customerId: string, amount: number, paymentMethod: string) {
    this.orderEvents.next({
      type: 'payment-processed',
      data: {
        orderId,
        customerId,
        amount,
        paymentMethod,
        processedAt: new Date(),
      },
    });
  }

  getOrderEventsForCustomer(customerId: string): Observable<MessageEvent> {
    return this.orderEvents.pipe(
      filter((event) => event.data.customerId === customerId),
      map((event) => ({
        data: JSON.stringify(event.data),
        type: event.type,
      } as MessageEvent))
    );
  }
}

// controllers/orders.controller.ts
import { Controller, Sse, Query, Post, Body } from '@nestjs/common';
import { Observable } from 'rxjs';
import { ApiSse } from '@nestjsvn/swagger-sse';
import { ApiQuery, ApiBody, ApiTags } from '@nestjs/swagger';
import { OrderService } from '../services/order.service';

@Controller('orders')
@ApiTags('Orders')
export class OrdersController {
  constructor(private orderService: OrderService) {}

  @Sse('customer/:customerId')
  @ApiQuery({ name: 'customerId', description: 'Customer ID to track orders for' })
  @ApiSse({
    summary: 'Customer order tracking',
    description: 'Real-time order updates for a specific customer',
    events: {
      'order-created': OrderCreatedDto,
      'order-status-updated': OrderStatusUpdatedDto,
      'payment-processed': PaymentProcessedDto,
    },
  })
  streamOrderEvents(@Query('customerId') customerId: string): Observable<MessageEvent> {
    return this.orderService.getOrderEventsForCustomer(customerId);
  }

  @Post('create')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        customerId: { type: 'string' },
        items: { type: 'array', items: { type: 'object' } },
        total: { type: 'number' },
      },
    },
  })
  createOrder(@Body() body: any) {
    const orderId = this.orderService.createOrder(body.customerId, body.items, body.total);
    return { orderId };
  }

  @Post('update-status')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        orderId: { type: 'string' },
        customerId: { type: 'string' },
        status: { type: 'string' },
      },
    },
  })
  updateOrderStatus(@Body() body: any) {
    this.orderService.updateOrderStatus(body.orderId, body.customerId, body.status);
    return { success: true };
  }
}
```

These examples demonstrate various real-world use cases for `@nestjsvn/swagger-sse`, from simple notifications to complex multi-event systems. Each example includes proper TypeScript typing, error handling, and comprehensive Swagger documentation.