Ejemplos de Red Web TypeScript

Ejemplos de red Web TypeScript incluyendo solicitudes HTTP, conexiones WebSocket y comunicación API

💻 Utilidades de Red typescript

🟢 simple ⭐⭐

Funciones utilitarias para manipulación de URL, manejo de cadenas de consulta, validación de solicitudes y monitoreo de estado de red

⏱️ 20 min 🏷️ typescript, web, networking, utilities
Prerequisites: Basic TypeScript, URL API
// Web TypeScript Network Utilities Examples
// Utility functions for common networking tasks

// 1. URL Utilities
class UrlUtilities {
  // Parse URL
  static parse(url: string): URL {
    return new URL(url);
  }

  // Build URL with query parameters
  static buildUrl(baseUrl: string, params: Record<string, string | number>): string {
    const url = new URL(baseUrl);
    Object.entries(params).forEach(([key, value]) => {
      url.searchParams.set(key, String(value));
    });
    return url.toString();
  }

  // Get query parameters from URL
  static getQueryParams(url: string): Record<string, string> {
    const urlObj = new URL(url);
    const params: Record<string, string> = {};

    urlObj.searchParams.forEach((value, key) => {
      params[key] = value;
    });

    return params;
  }

  // Update query parameters
  static updateQueryParams(
    url: string,
    updates: Record<string, string | number | null>
  ): string {
    const urlObj = new URL(url);

    Object.entries(updates).forEach(([key, value]) => {
      if (value === null) {
        urlObj.searchParams.delete(key);
      } else {
        urlObj.searchParams.set(key, String(value));
      }
    });

    return urlObj.toString();
  }

  // Remove query parameters
  static removeQueryParams(url: string, keys: string[]): string {
    const urlObj = new URL(url);
    keys.forEach(key => urlObj.searchParams.delete(key));
    return urlObj.toString();
  }

  // Check if URL is valid
  static isValid(url: string): boolean {
    try {
      new URL(url);
      return true;
    } catch {
      return false;
    }
  }

  // Get domain from URL
  static getDomain(url: string): string {
    const urlObj = new URL(url);
    return urlObj.hostname;
  }

  // Get path from URL
  static getPath(url: string): string {
    const urlObj = new URL(url);
    return urlObj.pathname;
  }
}

// 2. Query String Utilities
class QueryStringUtilities {
  // Serialize object to query string
  static stringify(obj: Record<string, any>): string {
    const params = new URLSearchParams();

    Object.entries(obj).forEach(([key, value]) => {
      if (value !== null && value !== undefined) {
        if (Array.isArray(value)) {
          value.forEach(v => params.append(key, String(v)));
        } else {
          params.set(key, String(value));
        }
      }
    });

    return params.toString();
  }

  // Parse query string to object
  static parse(queryString: string): Record<string, string | string[]> {
    const params = new URLSearchParams(queryString);
    const result: Record<string, string | string[]> = {};

    params.forEach((value, key) => {
      if (key in result) {
        const existing = result[key];
        if (Array.isArray(existing)) {
          existing.push(value);
        } else {
          result[key] = [existing, value];
        }
      } else {
        result[key] = value;
      }
    });

    return result;
  }

  // Merge query strings
  static merge(...queryStrings: string[]): string {
    const mergedParams = new URLSearchParams();

    queryStrings.forEach(qs => {
      const params = new URLSearchParams(qs);
      params.forEach((value, key) => {
        mergedParams.append(key, value);
      });
    });

    return mergedParams.toString();
  }
}

// 3. Network Status Monitor
class NetworkStatusMonitor {
  private isOnline: boolean = navigator.onLine;
  private listeners: Array<(online: boolean) => void> = [];

  constructor() {
    this.setupEventListeners();
  }

  private setupEventListeners(): void {
    window.addEventListener('online', () => {
      this.isOnline = true;
      this.notifyListeners(true);
    });

    window.addEventListener('offline', () => {
      this.isOnline = false;
      this.notifyListeners(false);
    });
  }

  private notifyListeners(online: boolean): void {
    this.listeners.forEach(listener => listener(online));
  }

  // Get current status
  getStatus(): boolean {
    return this.isOnline;
  }

  // Subscribe to status changes
  subscribe(listener: (online: boolean) => void): () => void {
    this.listeners.push(listener);

    // Return unsubscribe function
    return () => {
      const index = this.listeners.indexOf(listener);
      if (index > -1) {
        this.listeners.splice(index, 1);
      }
    };
  }

  // Wait for connection
  async waitForConnection(timeout: number = 30000): Promise<boolean> {
    if (this.isOnline) {
      return true;
    }

    return new Promise((resolve) => {
      const timeoutId = setTimeout(() => {
        unsubscribe();
        resolve(false);
      }, timeout);

      const unsubscribe = this.subscribe((online) => {
        if (online) {
          clearTimeout(timeoutId);
          unsubscribe();
          resolve(true);
        }
      });
    });
  }
}

// 4. Request Validation
class RequestValidator {
  // Validate URL
  static validateUrl(url: string): { valid: boolean; error?: string } {
    if (!url) {
      return { valid: false, error: 'URL is required' };
    }

    try {
      new URL(url);
      return { valid: true };
    } catch (error) {
      return { valid: false, error: 'Invalid URL format' };
    }
  }

  // Validate HTTP method
  static validateMethod(method: string): { valid: boolean; error?: string } {
    const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];

    if (!validMethods.includes(method.toUpperCase())) {
      return { valid: false, error: `Invalid HTTP method: ${method}` };
    }

    return { valid: true };
  }

  // Validate headers
  static validateHeaders(headers: Record<string, string>): { valid: boolean; errors: string[] } {
    const errors: string[] = [];

    Object.entries(headers).forEach(([key, value]) => {
      // Check for invalid characters in header name
      if (/[^\w\-!#$%&'*+.^_`|~]/.test(key)) {
        errors.push(`Invalid header name: ${key}`);
      }

      // Check header value
      if (/[-]/.test(value)) {
        errors.push(`Invalid header value for: ${key}`);
      }
    });

    return {
      valid: errors.length === 0,
      errors
    };
  }

  // Validate request body
  static validateBody(body: any, maxLength: number = 1024 * 1024): { valid: boolean; error?: string } {
    const bodyString = JSON.stringify(body);

    if (bodyString.length > maxLength) {
      return { valid: false, error: `Request body too large (max ${maxLength} bytes)` };
    }

    try {
      JSON.parse(bodyString);
      return { valid: true };
    } catch {
      return { valid: false, error: 'Invalid JSON in request body' };
    }
  }
}

// 5. CORS Utilities
class CorsUtilities {
  // Check if CORS is needed
  static isCrossOrigin(url: string): boolean {
    const targetOrigin = new URL(url).origin;
    const currentOrigin = window.location.origin;
    return targetOrigin !== currentOrigin;
  }

  // Build CORS request
  static buildCorsRequest(url: string, options: RequestInit = {}): RequestInit {
    return {
      ...options,
      mode: 'cors',
      headers: {
        ...(options.headers || {}),
        'Content-Type': 'application/json'
      }
    };
  }

  // Check if preflight request is needed
  static isPreflightRequired(method: string, headers?: Record<string, string>): boolean {
    const nonSimpleMethods = ['PUT', 'DELETE', 'PATCH', 'CONNECT', 'TRACE', 'OPTIONS'];

    if (nonSimpleMethods.includes(method.toUpperCase())) {
      return true;
    }

    if (headers) {
      const nonSimpleHeaders = ['X-Requested-With', 'X-HTTP-Method-Override'];
      return Object.keys(headers).some(h =>
        nonSimpleHeaders.some(nsh => h.toLowerCase().startsWith(nsh.toLowerCase()))
      );
    }

    return false;
  }
}

// 6. Request Retry Logic
class RequestRetryHandler {
  // Retry with exponential backoff
  static async retryWithBackoff<T>(
    fn: () => Promise<T>,
    maxRetries: number = 3,
    baseDelay: number = 1000
  ): Promise<T> {
    let lastError: Error;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error as Error;

        if (attempt < maxRetries) {
          const delay = baseDelay * Math.pow(2, attempt);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }

    throw lastError!;
  }

  // Retry on specific status codes
  static async retryOnStatus<T>(
    fn: () => Promise<Response>,
    retryStatuses: number[] = [408, 429, 500, 502, 503, 504],
    maxRetries: number = 3
  ): Promise<Response> {
    let lastResponse: Response;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      const response = await fn();
      lastResponse = response;

      if (!retryStatuses.includes(response.status)) {
        return response;
      }

      if (attempt < maxRetries) {
        await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
      }
    }

    return lastResponse!;
  }
}

// 7. Connection Pool Manager (for managing multiple concurrent requests)
class ConnectionPool {
  private activeRequests: Set<Promise<any>> = new Set();
  private maxConcurrent: number;

  constructor(maxConcurrent: number = 6) {
    this.maxConcurrent = maxConcurrent;
  }

  // Execute request with pool management
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    // Wait if pool is full
    while (this.activeRequests.size >= this.maxConcurrent) {
      await Promise.race(this.activeRequests);
    }

    const request = fn().finally(() => {
      this.activeRequests.delete(request);
    });

    this.activeRequests.add(request);
    return request;
  }

  // Get pool size
  get size(): number {
    return this.activeRequests.size;
  }

  // Get available slots
  get available(): number {
    return this.maxConcurrent - this.activeRequests.size;
  }
}

// 8. Network Request Logger
class NetworkRequestLogger {
  private logs: Array<{
    timestamp: number;
    url: string;
    method: string;
    status?: number;
    duration: number;
    success: boolean;
  }> = [];

  // Log request
  async logRequest<T>(
    fn: () => Promise<T>,
    url: string,
    method: string = 'GET'
  ): Promise<T> {
    const startTime = Date.now();
    const timestamp = startTime;

    try {
      const result = await fn();
      const duration = Date.now() - startTime;

      this.logs.push({
        timestamp,
        url,
        method,
        status: 200,
        duration,
        success: true
      });

      return result;
    } catch (error) {
      const duration = Date.now() - startTime;

      this.logs.push({
        timestamp,
        url,
        method,
        status: 0,
        duration,
        success: false
      });

      throw error;
    }
  }

  // Get all logs
  getLogs(): typeof this.logs {
    return [...this.logs];
  }

  // Get failed requests
  getFailedRequests(): typeof this.logs {
    return this.logs.filter(log => !log.success);
  }

  // Get slow requests (above threshold)
  getSlowRequests(threshold: number = 1000): typeof this.logs {
    return this.logs.filter(log => log.duration > threshold);
  }

  // Get statistics
  getStats(): {
    total: number;
    successful: number;
    failed: number;
    avgDuration: number;
    slowest: number;
    fastest: number;
  } {
    const successful = this.logs.filter(l => l.success).length;
    const failed = this.logs.filter(l => !l.success).length;
    const durations = this.logs.map(l => l.duration);

    return {
      total: this.logs.length,
      successful,
      failed,
      avgDuration: durations.length ? durations.reduce((a, b) => a + b) / durations.length : 0,
      slowest: durations.length ? Math.max(...durations) : 0,
      fastest: durations.length ? Math.min(...durations) : 0
    };
  }

  // Clear logs
  clearLogs(): void {
    this.logs = [];
  }
}

// Usage Examples
async function demonstrateNetworkUtilities() {
  console.log('=== Web TypeScript Network Utilities Examples ===\n');

  // 1. URL utilities
  console.log('--- 1. URL Utilities ---');
  const url = 'https://example.com/api/users?page=1&limit=10';

  console.log(`Parsed URL:`, UrlUtilities.parse(url));
  console.log(`Domain:`, UrlUtilities.getDomain(url));
  console.log(`Path:`, UrlUtilities.getPath(url));
  console.log(`Query params:`, UrlUtilities.getQueryParams(url));

  const updatedUrl = UrlUtilities.updateQueryParams(url, { page: 2, filter: 'active' });
  console.log(`Updated URL:`, updatedUrl);

  // 2. Query string utilities
  console.log('\n--- 2. Query String Utilities ---');
  const obj = { search: 'test', page: 1, filters: ['a', 'b'] };
  const queryString = QueryStringUtilities.stringify(obj);
  console.log(`Stringified:`, queryString);

  const parsed = QueryStringUtilities.parse(queryString);
  console.log(`Parsed:`, parsed);

  // 3. Network status
  console.log('\n--- 3. Network Status ---');
  const monitor = new NetworkStatusMonitor();
  console.log(`Currently online: ${monitor.getStatus()}`);

  const unsubscribe = monitor.subscribe((online) => {
    console.log(`Network status changed: ${online ? 'online' : 'offline'}`);
  });

  console.log('Subscribed to network status changes');

  // 4. Request validation
  console.log('\n--- 4. Request Validation ---');
  const urlValidation = RequestValidator.validateUrl('https://example.com');
  console.log(`URL valid: ${urlValidation.valid}`);

  const methodValidation = RequestValidator.validateMethod('GET');
  console.log(`Method valid: ${methodValidation.valid}`);

  const headersValidation = RequestValidator.validateHeaders({
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  });
  console.log(`Headers valid: ${headersValidation.valid}`);

  // 5. CORS utilities
  console.log('\n--- 5. CORS Utilities ---');
  const crossOrigin = CorsUtilities.isCrossOrigin('https://api.example.com/data');
  console.log(`Is cross-origin: ${crossOrigin}`);

  const needsPreflight = CorsUtilities.isPreflightRequired('PUT', { 'X-Custom-Header': 'value' });
  console.log(`Needs preflight: ${needsPreflight}`);

  // 6. Request retry
  console.log('\n--- 6. Request Retry ---');
  let attempts = 0;
  const result = await RequestRetryHandler.retryWithBackoff(async () => {
    attempts++;
    console.log(`Attempt ${attempts}`);

    if (attempts < 2) {
      throw new Error('Temporary failure');
    }

    return 'Success!';
  }, 3, 100);

  console.log(`Retry result: ${result}`);

  // 7. Connection pool
  console.log('\n--- 7. Connection Pool ---');
  const pool = new ConnectionPool(3);

  const poolPromises = Array.from({ length: 5 }, (_, i) =>
    pool.execute(async () => {
      console.log(`Request ${i + 1} started (pool size: ${pool.size})`);
      await new Promise(resolve => setTimeout(resolve, 100));
      console.log(`Request ${i + 1} completed`);
      return i;
    })
  );

  await Promise.all(poolPromises);

  // 8. Request logger
  console.log('\n--- 8. Request Logger ---');
  const logger = new NetworkRequestLogger();

  await logger.logRequest(
    async () => {
      await new Promise(resolve => setTimeout(resolve, 50));
      return 'data';
    },
    'https://api.example.com/data',
    'GET'
  );

  const stats = logger.getStats();
  console.log(`Logger stats:`, stats);

  console.log('\n=== All Network Utilities Examples Completed ===');
}

// Export classes
export { UrlUtilities, QueryStringUtilities, NetworkStatusMonitor, RequestValidator, CorsUtilities, RequestRetryHandler, ConnectionPool, NetworkRequestLogger };
export { demonstrateNetworkUtilities };

💻 Solicitudes HTTP typescript

🟡 intermediate ⭐⭐⭐

Enviar solicitudes GET, POST, PUT, DELETE usando Fetch API con manejo de errores y análisis de respuesta

⏱️ 25 min 🏷️ typescript, web, networking, http
Prerequisites: Intermediate TypeScript, Fetch API
// Web TypeScript HTTP Requests Examples
// Using Fetch API for HTTP communication with servers

// 1. Basic HTTP Request Methods
class HttpClient {
  private baseURL: string;
  private defaultHeaders: Record<string, string>;

  constructor(baseURL: string = '', defaultHeaders: Record<string, string> = {}) {
    this.baseURL = baseURL;
    this.defaultHeaders = {
      'Content-Type': 'application/json',
      ...defaultHeaders
    };
  }

  // GET request
  async get<T>(url: string, headers?: Record<string, string>): Promise<T> {
    const response = await fetch(`${this.baseURL}${url}`, {
      method: 'GET',
      headers: { ...this.defaultHeaders, ...headers }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }

  // POST request
  async post<T>(url: string, data: any, headers?: Record<string, string>): Promise<T> {
    const response = await fetch(`${this.baseURL}${url}`, {
      method: 'POST',
      headers: { ...this.defaultHeaders, ...headers },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }

  // PUT request
  async put<T>(url: string, data: any, headers?: Record<string, string>): Promise<T> {
    const response = await fetch(`${this.baseURL}${url}`, {
      method: 'PUT',
      headers: { ...this.defaultHeaders, ...headers },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }

  // DELETE request
  async delete<T>(url: string, headers?: Record<string, string>): Promise<T> {
    const response = await fetch(`${this.baseURL}${url}`, {
      method: 'DELETE',
      headers: { ...this.defaultHeaders, ...headers }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }

  // PATCH request
  async patch<T>(url: string, data: any, headers?: Record<string, string>): Promise<T> {
    const response = await fetch(`${this.baseURL}${url}`, {
      method: 'PATCH',
      headers: { ...this.defaultHeaders, ...headers },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }
}

// 2. Advanced HTTP Features
class AdvancedHttpClient {
  // Request with query parameters
  async getWithQueryParams<T>(
    url: string,
    params: Record<string, string | number>
  ): Promise<T> {
    const queryString = new URLSearchParams(
      Object.entries(params).map(([k, v]) => [k, String(v)])
    ).toString();

    const response = await fetch(`${url}?${queryString}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return response.json();
  }

  // Request with timeout
  async fetchWithTimeout<T>(
    url: string,
    options: RequestInit = {},
    timeout: number = 5000
  ): Promise<T> {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

    try {
      const response = await fetch(url, {
        ...options,
        signal: controller.signal
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      return response.json();
    } finally {
      clearTimeout(timeoutId);
    }
  }

  // Upload file with progress
  async uploadFile(
    url: string,
    file: File,
    onProgress?: (progress: number) => void
  ): Promise<Response> {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();

      xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable && onProgress) {
          const progress = Math.round((e.loaded / e.total) * 100);
          onProgress(progress);
        }
      });

      xhr.addEventListener('load', () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(new Response(xhr.responseText, { status: xhr.status }));
        } else {
          reject(new Error(`HTTP error! status: ${xhr.status}`));
        }
      });

      xhr.addEventListener('error', () => {
        reject(new Error('Network error'));
      });

      xhr.open('POST', url);
      xhr.send(file);
    });
  }

  // Download file
  async downloadFile(url: string, fileName: string): Promise<void> {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const blob = await response.blob();
    const blobUrl = URL.createObjectURL(blob);

    const link = document.createElement('a');
    link.href = blobUrl;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(blobUrl);
  }

  // Request with retry
  async fetchWithRetry<T>(
    url: string,
    options: RequestInit = {},
    maxRetries: number = 3,
    delayMs: number = 1000
  ): Promise<T> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const response = await fetch(url, options);
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
      } catch (error) {
        if (attempt === maxRetries) {
          throw error;
        }
        await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
      }
    }

    throw new Error('Max retries exceeded');
  }
}

// 3. Request/Response Interceptors
class HttpClientWithInterceptors {
  private requestInterceptors: Array<(config: RequestInit) => RequestInit> = [];
  private responseInterceptors: Array<(response: Response) => Promise<Response>> = [];

  // Add request interceptor
  addRequestInterceptor(interceptor: (config: RequestInit) => RequestInit): void {
    this.requestInterceptors.push(interceptor);
  }

  // Add response interceptor
  addResponseInterceptor(interceptor: (response: Response) => Promise<Response>): void {
    this.responseInterceptors.push(interceptor);
  }

  // Execute request with interceptors
  async fetchWithInterceptors(url: string, options: RequestInit = {}): Promise<Response> {
    let config = options;

    // Apply request interceptors
    for (const interceptor of this.requestInterceptors) {
      config = interceptor(config);
    }

    let response = await fetch(url, config);

    // Apply response interceptors
    for (const interceptor of this.responseInterceptors) {
      response = await interceptor(response);
    }

    return response;
  }
}

// 4. API Client Builder
class ApiClientBuilder {
  private baseURL: string = '';
  private headers: Record<string, string> = {};
  private interceptors: {
    request: Array<(config: RequestInit) => RequestInit>;
    response: Array<(response: Response) => Promise<Response>>;
  } = { request: [], response: [] };

  setBaseURL(url: string): this {
    this.baseURL = url;
    return this;
  }

  setHeader(key: string, value: string): this {
    this.headers[key] = value;
    return this;
  }

  setHeaders(headers: Record<string, string>): this {
    this.headers = { ...this.headers, ...headers };
    return this;
  }

  addRequestInterceptor(interceptor: (config: RequestInit) => RequestInit): this {
    this.interceptors.request.push(interceptor);
    return this;
  }

  addResponseInterceptor(interceptor: (response: Response) => Promise<Response>): this {
    this.interceptors.response.push(interceptor);
    return this;
  }

  build(): HttpClient {
    const client = new HttpClient(this.baseURL, this.headers);

    // Apply interceptors to client if needed
    // This is a simplified version

    return client;
  }
}

// 5. REST API Client
class RestClient {
  private client: HttpClient;

  constructor(baseURL: string, authToken?: string) {
    const headers: Record<string, string> = {};
    if (authToken) {
      headers['Authorization'] = `Bearer ${authToken}`;
    }

    this.client = new HttpClient(baseURL, headers);
  }

  // CRUD operations
  async getAll<T>(resource: string): Promise<T[]> {
    return this.client.get<T[]>(`/${resource}`);
  }

  async getById<T>(resource: string, id: string | number): Promise<T> {
    return this.client.get<T>(`/${resource}/${id}`);
  }

  async create<T>(resource: string, data: any): Promise<T> {
    return this.client.post<T>(`/${resource}`, data);
  }

  async update<T>(resource: string, id: string | number, data: any): Promise<T> {
    return this.client.put<T>(`/${resource}/${id}`, data);
  }

  async patch<T>(resource: string, id: string | number, data: any): Promise<T> {
    return this.client.patch<T>(`/${resource}/${id}`, data);
  }

  async delete<T>(resource: string, id: string | number): Promise<T> {
    return this.client.delete<T>(`/${resource}/${id}`);
  }

  // Query operations
  async query<T>(resource: string, params: Record<string, any>): Promise<T[]> {
    const advancedClient = new AdvancedHttpClient();
    return advancedClient.getWithQueryParams<T[]>(`${this.client['baseURL']}/${resource}`, params);
  }
}

// 6. GraphQL Client
class GraphQLClient {
  private endpoint: string;
  private headers: Record<string, string>;

  constructor(endpoint: string, headers: Record<string, string> = {}) {
    this.endpoint = endpoint;
    this.headers = {
      'Content-Type': 'application/json',
      ...headers
    };
  }

  async query<T>(query: string, variables?: Record<string, any>): Promise<T> {
    const response = await fetch(this.endpoint, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify({
        query,
        variables
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();

    if (result.errors) {
      throw new Error(result.errors[0].message);
    }

    return result.data;
  }

  async mutate<T>(mutation: string, variables?: Record<string, any>): Promise<T> {
    return this.query<T>(mutation, variables);
  }
}

// 7. Response Caching
class CachedHttpClient {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private cacheTimeout: number;

  constructor(cacheTimeout: number = 60000) {
    this.cacheTimeout = cacheTimeout;
  }

  async get<T>(url: string, useCache: boolean = true): Promise<T> {
    if (useCache && this.cache.has(url)) {
      const cached = this.cache.get(url)!;
      const age = Date.now() - cached.timestamp;

      if (age < this.cacheTimeout) {
        return cached.data as T;
      }
    }

    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    this.cache.set(url, { data, timestamp: Date.now() });

    return data as T;
  }

  clearCache(): void {
    this.cache.clear();
  }

  clearCacheEntry(url: string): void {
    this.cache.delete(url);
  }
}

// 8. Batch Requests
class BatchRequestClient {
  // Execute multiple requests concurrently
  async fetchAll<T>(requests: Array<{ url: string; options?: RequestInit }>): Promise<T[]> {
    const promises = requests.map(req => fetch(req.url, req.options));
    const responses = await Promise.all(promises);

    const results: T[] = [];
    for (const response of responses) {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      results.push(await response.json());
    }

    return results;
  }

  // Execute requests in batches
  async fetchInBatches<T>(
    requests: Array<{ url: string; options?: RequestInit }>,
    batchSize: number = 5
  ): Promise<T[]> {
    const results: T[] = [];

    for (let i = 0; i < requests.length; i += batchSize) {
      const batch = requests.slice(i, i + batchSize);
      const batchResults = await this.fetchAll<T>(batch);
      results.push(...batchResults);
    }

    return results;
  }
}

// Usage Examples
async function demonstrateHttpRequests() {
  console.log('=== Web TypeScript HTTP Requests Examples ===\n');

  // 1. Basic HTTP methods
  console.log('--- 1. Basic HTTP Methods ---');
  const client = new HttpClient('https://jsonplaceholder.typicode.com');

  try {
    const posts = await client.get('/posts');
    console.log(`GET /posts: Retrieved ${(posts as any).length} posts`);

    const newPost = await client.post('/posts', {
      title: 'Test Post',
      body: 'This is a test post',
      userId: 1
    });
    console.log(`POST /posts: Created post with ID ${(newPost as any).id}`);
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  // 2. Query parameters
  console.log('\n--- 2. Query Parameters ---');
  const advancedClient = new AdvancedHttpClient();

  try {
    const filteredPosts = await advancedClient.getWithQueryParams(
      'https://jsonplaceholder.typicode.com/posts',
      { userId: 1, _limit: 5 }
    );
    console.log(`Filtered posts: ${(filteredPosts as any).length} results`);
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  // 3. Request with timeout
  console.log('\n--- 3. Request with Timeout ---');
  try {
    const result = await advancedClient.fetchWithTimeout(
      'https://jsonplaceholder.typicode.com/posts/1',
      {},
      3000
    );
    console.log(`Request completed: ${JSON.stringify(result).substring(0, 100)}...`);
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  // 4. REST API client
  console.log('\n--- 4. REST API Client ---');
  const restClient = new RestClient('https://jsonplaceholder.typicode.com');

  try {
    const todos = await restClient.getAll('todos');
    console.log(`GET /todos: ${todos.length} items`);

    const todo = await restClient.getById('todos', 1);
    console.log(`GET /todos/1: ${todo.title}`);

    const newTodo = await restClient.create('todos', {
      title: 'New Todo',
      completed: false
    });
    console.log(`POST /todos: Created ID ${newTodo.id}`);
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  // 5. GraphQL query
  console.log('\n--- 5. GraphQL Query ---');
  const graphqlClient = new GraphQLClient('https://api.github.com/graphql', {
    Authorization: 'Bearer YOUR_TOKEN'
  });

  const query = `
    query {
      viewer {
        login
        name
      }
    }
  `;

  console.log('GraphQL query prepared (requires auth token)');

  // 6. Cached requests
  console.log('\n--- 6. Cached Requests ---');
  const cachedClient = new CachedHttpClient(60000);

  try {
    const start1 = Date.now();
    await cachedClient.get('https://jsonplaceholder.typicode.com/users');
    const time1 = Date.now() - start1;

    const start2 = Date.now();
    await cachedClient.get('https://jsonplaceholder.typicode.com/users');
    const time2 = Date.now() - start2;

    console.log(`First request: ${time1}ms`);
    console.log(`Cached request: ${time2}ms`);
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  // 7. Batch requests
  console.log('\n--- 7. Batch Requests ---');
  const batchClient = new BatchRequestClient();

  try {
    const batchResults = await batchClient.fetchAll([
      { url: 'https://jsonplaceholder.typicode.com/posts/1' },
      { url: 'https://jsonplaceholder.typicode.com/posts/2' },
      { url: 'https://jsonplaceholder.typicode.com/posts/3' }
    ]);
    console.log(`Batch request completed: ${batchResults.length} results`);
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  console.log('\n=== All HTTP Requests Examples Completed ===');
}

// Export classes
export { HttpClient, AdvancedHttpClient, HttpClientWithInterceptors, ApiClientBuilder, RestClient, GraphQLClient, CachedHttpClient, BatchRequestClient };
export { demonstrateHttpRequests };

💻 Comunicación WebSocket typescript

🟡 intermediate ⭐⭐⭐

Establecer comunicación bidireccional en tiempo real usando WebSocket API con reconexión y manejo de mensajes

⏱️ 30 min 🏷️ typescript, web, networking, websocket
Prerequisites: Intermediate TypeScript, WebSocket API
// Web TypeScript WebSocket Communication Examples
// Real-time bidirectional communication using WebSocket API

// 1. Basic WebSocket Connection
class WebSocketClient {
  private ws: WebSocket | null = null;
  private url: string;
  private reconnectAttempts: number = 0;
  private maxReconnectAttempts: number = 5;
  private reconnectDelay: number = 1000;

  constructor(url: string) {
    this.url = url;
  }

  // Connect to WebSocket server
  connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.ws = new WebSocket(this.url);

      this.ws.onopen = () => {
        console.log('WebSocket connected');
        this.reconnectAttempts = 0;
        resolve();
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        reject(error);
      };

      this.ws.onclose = () => {
        console.log('WebSocket disconnected');
        this.attemptReconnect();
      };
    });
  }

  // Send message
  send(data: string | object): void {
    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
      throw new Error('WebSocket is not connected');
    }

    const message = typeof data === 'string' ? data : JSON.stringify(data);
    this.ws.send(message);
  }

  // Receive message
  onMessage(callback: (data: any) => void): void {
    if (!this.ws) {
      throw new Error('WebSocket is not initialized');
    }

    this.ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        callback(data);
      } catch {
        callback(event.data);
      }
    };
  }

  // Close connection
  close(): void {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }

  // Attempt to reconnect
  private attemptReconnect(): void {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = this.reconnectDelay * this.reconnectAttempts;

      console.log(`Reconnecting in ${delay}ms... (attempt ${this.reconnectAttempts})`);

      setTimeout(() => {
        this.connect();
      }, delay);
    } else {
      console.error('Max reconnection attempts reached');
    }
  }

  // Get connection state
  get readyState(): number {
    return this.ws?.readyState ?? WebSocket.CLOSED;
  }
}

// 2. Advanced WebSocket with Message Queuing
class AdvancedWebSocketClient {
  private ws: WebSocket | null = null;
  private messageQueue: Array<string | object> = [];
  private isManuallyClosed: boolean = false;
  private messageHandlers: Map<string, Array<(data: any) => void>> = new Map();
  private reconnectTimeout: number = 5000;
  private pingInterval: NodeJS.Timeout | null = null;

  constructor(private url: string) {}

  // Connect with automatic reconnection
  async connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.ws = new WebSocket(this.url);

      this.ws.onopen = () => {
        console.log('WebSocket connected');
        this.startPing();
        this.flushMessageQueue();
        resolve();
      };

      this.ws.onmessage = (event) => {
        this.handleMessage(event);
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        reject(error);
      };

      this.ws.onclose = () => {
        console.log('WebSocket disconnected');
        this.stopPing();
        if (!this.isManuallyClosed) {
          setTimeout(() => this.connect(), this.reconnectTimeout);
        }
      };
    });
  }

  // Send message immediately or queue it
  send(data: string | object, type?: string): void {
    const message = type ? { type, data } : data;

    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    } else {
      this.messageQueue.push(message);
      console.log('Message queued');
    }
  }

  // Flush queued messages
  private flushMessageQueue(): void {
    while (this.messageQueue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
      const message = this.messageQueue.shift();
      this.ws.send(JSON.stringify(message));
    }
  }

  // Handle incoming message
  private handleMessage(event: MessageEvent): void {
    try {
      const message = JSON.parse(event.data);

      if (message.type && this.messageHandlers.has(message.type)) {
        const handlers = this.messageHandlers.get(message.type)!;
        handlers.forEach(handler => handler(message.data));
      }
    } catch (error) {
      console.error('Failed to parse message:', error);
    }
  }

  // Register message handler
  on(type: string, callback: (data: any) => void): void {
    if (!this.messageHandlers.has(type)) {
      this.messageHandlers.set(type, []);
    }
    this.messageHandlers.get(type)!.push(callback);
  }

  // Remove message handler
  off(type: string, callback: (data: any) => void): void {
    const handlers = this.messageHandlers.get(type);
    if (handlers) {
      const index = handlers.indexOf(callback);
      if (index > -1) {
        handlers.splice(index, 1);
      }
    }
  }

  // Start ping to keep connection alive
  private startPing(): void {
    this.pingInterval = setInterval(() => {
      this.send({ type: 'ping', timestamp: Date.now() });
    }, 30000);
  }

  // Stop ping
  private stopPing(): void {
    if (this.pingInterval) {
      clearInterval(this.pingInterval);
      this.pingInterval = null;
    }
  }

  // Close connection
  close(): void {
    this.isManuallyClosed = true;
    this.stopPing();
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }
}

// 3. WebSocket Room/Channel Management
class WebSocketRoomClient {
  private client: AdvancedWebSocketClient;
  private currentRooms: Set<string> = new Set();

  constructor(url: string) {
    this.client = new AdvancedWebSocketClient(url);
  }

  // Connect to server
  async connect(): Promise<void> {
    await this.client.connect();
  }

  // Join room
  joinRoom(roomName: string): void {
    if (!this.currentRooms.has(roomName)) {
      this.client.send({ type: 'join', room: roomName });
      this.currentRooms.add(roomName);
      console.log(`Joined room: ${roomName}`);
    }
  }

  // Leave room
  leaveRoom(roomName: string): void {
    if (this.currentRooms.has(roomName)) {
      this.client.send({ type: 'leave', room: roomName });
      this.currentRooms.delete(roomName);
      console.log(`Left room: ${roomName}`);
    }
  }

  // Send message to room
  sendToRoom(roomName: string, message: any): void {
    this.client.send({
      type: 'room_message',
      room: roomName,
      data: message
    });
  }

  // Listen to room messages
  onRoomMessage(roomName: string, callback: (message: any) => void): void {
    this.client.on('room_message', (data) => {
      if (data.room === roomName) {
        callback(data.message);
      }
    });
  }

  // Leave all rooms
  leaveAllRooms(): void {
    this.currentRooms.forEach(room => this.leaveRoom(room));
  }

  // Close connection
  close(): void {
    this.leaveAllRooms();
    this.client.close();
  }
}

// 4. WebSocket with Authentication
class AuthenticatedWebSocketClient {
  private ws: WebSocket | null = null;
  private authToken: string;
  private reconnectOnAuthFailure: boolean = true;

  constructor(
    private url: string,
    authToken: string
  ) {
    this.authToken = authToken;
  }

  // Connect with authentication
  async connect(): Promise<void> {
    const wsUrl = `${this.url}?token=${this.authToken}`;

    return new Promise((resolve, reject) => {
      this.ws = new WebSocket(wsUrl);

      this.ws.onopen = () => {
        console.log('Authenticated WebSocket connected');
        resolve();
      };

      this.ws.onmessage = (event) => {
        const message = JSON.parse(event.data);

        // Handle authentication failure
        if (message.type === 'auth_error') {
          console.error('Authentication failed:', message.error);
          if (this.reconnectOnAuthFailure) {
            // Could trigger token refresh here
          }
          reject(new Error(message.error));
        }
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        reject(error);
      };

      this.ws.onclose = (event) => {
        console.log(`WebSocket closed: ${event.code}`);
      };
    });
  }

  // Send authenticated message
  send(data: any): void {
    if (this.ws?.readyState === WebSocket.OPEN) {
      const message = {
        ...data,
        token: this.authToken
      };
      this.ws.send(JSON.stringify(message));
    }
  }

  // Update authentication token
  updateToken(newToken: string): void {
    this.authToken = newToken;
  }

  // Close connection
  close(): void {
    this.ws?.close();
  }
}

// 5. Binary Data Handling
class BinaryWebSocketClient {
  private ws: WebSocket | null = null;

  constructor(private url: string) {}

  // Connect
  async connect(binaryType: BinaryType = 'blob'): Promise<void> {
    return new Promise((resolve, reject) => {
      this.ws = new WebSocket(this.url);
      this.ws.binaryType = binaryType;

      this.ws.onopen = () => resolve();
      this.ws.onerror = reject;
    });
  }

  // Send binary data
  sendBinary(data: ArrayBuffer | Blob): void {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    }
  }

  // Receive binary data
  onBinaryData(callback: (data: ArrayBuffer | Blob) => void): void {
    if (this.ws) {
      this.ws.onmessage = (event) => {
        callback(event.data);
      };
    }
  }

  // Close connection
  close(): void {
    this.ws?.close();
  }
}

// 6. WebSocket RPC (Remote Procedure Call)
class WebSocketRPCClient {
  private requestCallbacks: Map<number, (response: any) => void> = new Map();
  private requestId: number = 0;
  private client: AdvancedWebSocketClient;

  constructor(url: string) {
    this.client = new AdvancedWebSocketClient(url);
  }

  // Connect
  async connect(): Promise<void> {
    await this.client.connect();

    // Listen for RPC responses
    this.client.on('rpc_response', (data) => {
      const { id, result, error } = data;

      const callback = this.requestCallbacks.get(id);
      if (callback) {
        callback({ result, error });
        this.requestCallbacks.delete(id);
      }
    });
  }

  // Call remote method
  async call(method: string, params?: any): Promise<any> {
    const id = ++this.requestId;

    return new Promise((resolve, reject) => {
      this.requestCallbacks.set(id, ({ result, error }) => {
        if (error) {
          reject(new Error(error));
        } else {
          resolve(result);
        }
      });

      this.client.send({
        type: 'rpc_request',
        id,
        method,
        params
      });
    });
  }

  // Register remote method handler
  on(method: string, handler: (params: any) => any): void {
    this.client.on('rpc_request', async (data) => {
      if (data.method === method) {
        try {
          const result = await handler(data.params);
          this.client.send({
            type: 'rpc_response',
            id: data.id,
            result
          });
        } catch (error) {
          this.client.send({
            type: 'rpc_response',
            id: data.id,
            error: (error as Error).message
          });
        }
      }
    });
  }

  // Close connection
  close(): void {
    this.client.close();
  }
}

// 7. WebSocket Statistics and Monitoring
class MonitoredWebSocketClient {
  private client: WebSocketClient;
  private stats = {
    messagesSent: 0,
    messagesReceived: 0,
    bytesSent: 0,
    bytesReceived: 0,
    connectionTime: 0,
    reconnectionCount: 0
  };

  constructor(url: string) {
    this.client = new WebSocketClient(url);
  }

  // Connect with monitoring
  async connect(): Promise<void> {
    this.stats.connectionTime = Date.now();
    await this.client.connect();

    // Wrap message handler to monitor
    this.client.onMessage((data) => {
      this.stats.messagesReceived++;
      this.stats.bytesReceived += JSON.stringify(data).length;
    });
  }

  // Send with monitoring
  send(data: any): void {
    const message = JSON.stringify(data);
    this.stats.messagesSent++;
    this.stats.bytesSent += message.length;
    this.client.send(data);
  }

  // Get statistics
  getStats(): typeof this.stats {
    return {
      ...this.stats,
      uptime: Date.now() - this.stats.connectionTime
    };
  }

  // Print statistics
  printStats(): void {
    const stats = this.getStats();
    console.log('\n=== WebSocket Statistics ===');
    console.log(`Messages Sent: ${stats.messagesSent}`);
    console.log(`Messages Received: ${stats.messagesReceived}`);
    console.log(`Bytes Sent: ${stats.bytesSent}`);
    console.log(`Bytes Received: ${stats.bytesReceived}`);
    console.log(`Uptime: ${stats.uptime}ms`);
  }

  // Close connection
  close(): void {
    this.client.close();
  }
}

// Usage Examples
async function demonstrateWebSocket() {
  console.log('=== Web TypeScript WebSocket Examples ===\n');

  // Note: These examples require a WebSocket server to connect to
  // Replace 'ws://localhost:8080' with your WebSocket server URL

  // 1. Basic WebSocket
  console.log('--- 1. Basic WebSocket Connection ---');
  const basicClient = new WebSocketClient('ws://localhost:8080');

  try {
    // Uncomment to test with actual server
    // await basicClient.connect();
    // basicClient.send({ type: 'greeting', message: 'Hello Server!' });
    // basicClient.onMessage((data) => console.log('Received:', data));
    console.log('Basic WebSocket client created (requires server)');
  } catch (error) {
    console.error(`Error: ${(error as Error).message}`);
  }

  // 2. Advanced WebSocket with message handlers
  console.log('\n--- 2. Advanced WebSocket ---');
  const advancedClient = new AdvancedWebSocketClient('ws://localhost:8080');

  // Register message handlers
  advancedClient.on('chat', (data) => {
    console.log('Chat message:', data);
  });

  advancedClient.on('notification', (data) => {
    console.log('Notification:', data);
  });

  console.log('Advanced WebSocket client with handlers created');

  // 3. Room management
  console.log('\n--- 3. Room Management ---');
  const roomClient = new WebSocketRoomClient('ws://localhost:8080');

  // Uncomment to test with actual server
  // await roomClient.connect();
  // roomClient.joinRoom('general');
  // roomClient.sendToRoom('general', { text: 'Hello everyone!' });
  // roomClient.onRoomMessage('general', (msg) => console.log('Room message:', msg));

  console.log('Room client created');

  // 4. Authenticated WebSocket
  console.log('\n--- 4. Authenticated WebSocket ---');
  const authClient = new AuthenticatedWebSocketClient(
    'ws://localhost:8080',
    'your-auth-token'
  );

  console.log('Authenticated WebSocket client created');

  // 5. RPC client
  console.log('\n--- 5. WebSocket RPC ---');
  const rpcClient = new WebSocketRPCClient('ws://localhost:8080');

  // Uncomment to test with actual server
  // await rpcClient.connect();
  // const result = await rpcClient.call('add', { a: 5, b: 3 });
  // console.log('RPC result:', result);

  console.log('RPC client created');

  // 6. Monitoring
  console.log('\n--- 6. WebSocket Monitoring ---');
  const monitoredClient = new MonitoredWebSocketClient('ws://localhost:8080');

  console.log('Monitored WebSocket client created');

  console.log('\n=== All WebSocket Examples Completed ===');
  console.log('Note: These examples require a WebSocket server to connect to');
}

// Export classes
export { WebSocketClient, AdvancedWebSocketClient, WebSocketRoomClient, AuthenticatedWebSocketClient, BinaryWebSocketClient, WebSocketRPCClient, MonitoredWebSocketClient };
export { demonstrateWebSocket };