Exemples de Réseau Web TypeScript

Exemples de réseau Web TypeScript incluant les requêtes HTTP, les connexions WebSocket et la communication API

Key Facts

Category
TypeScript
Items
3
Format Families
text

Sample Overview

Exemples de réseau Web TypeScript incluant les requêtes HTTP, les connexions WebSocket et la communication API This sample set belongs to TypeScript and can be used to test related workflows inside Elysia Tools.

💻 Utilitaires Réseau typescript

🟢 simple ⭐⭐

Fonctions utilitaires pour la manipulation d'URL, le traitement des chaînes de requête, la validation des requêtes et la surveillance du statut réseau

⏱️ 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 };

💻 Requêtes HTTP typescript

🟡 intermediate ⭐⭐⭐

Envoyer des requêtes GET, POST, PUT, DELETE en utilisant Fetch API avec gestion des erreurs et analyse des réponses

⏱️ 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 this.parseResponse<T>(response);
  }

  // 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 this.parseResponse<T>(response);
  }

  // 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 this.parseResponse<T>(response);
  }

  // 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 this.parseResponse<T>(response);
  }

  // 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 this.parseResponse<T>(response);
  }

  private async parseResponse<T>(response: Response): Promise<T> {
    // 204/205 and empty bodies are valid and should not be parsed as JSON.
    if (response.status === 204 || response.status === 205) {
      return undefined as T;
    }

    const text = await response.text();
    if (!text) {
      return undefined as T;
    }

    return JSON.parse(text) as T;
  }
}

// 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 HttpClientResponseParser.parse<T>(response);
  }

  // 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 HttpClientResponseParser.parse<T>(response);
    } 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 HttpClientResponseParser.parse<T>(response);
      } 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;
  }
}

class HttpClientResponseParser {
  static async parse<T>(response: Response): Promise<T> {
    if (response.status === 204 || response.status === 205) {
      return undefined as T;
    }

    const text = await response.text();
    if (!text) {
      return undefined as T;
    }

    return JSON.parse(text) as T;
  }
}

// 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 };

💻 Communication WebSocket typescript

🟡 intermediate ⭐⭐⭐

Établir une communication bidirectionnelle en temps réel en utilisant WebSocket API avec reconnexion et gestion des messages

⏱️ 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: ReturnType<typeof setTimeout> | 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 };