npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hyperwiz

v1.2.2

Published

Next-generation TypeScript HTTP client with built-in retry, smart caching, and seamless authentication. Effortlessly handle API requests with advanced features for modern web development.

Readme

hyperwiz

npm version npm downloads TypeScript License

A lightweight, flexible HTTP client library with powerful interceptors for modern web applications.

✨ Features

  • 🚀 Simple Setup - One-line client creation
  • 🔧 Flexible Interceptors - Add request/response/error handlers
  • 🍪 Cookie Support - Works with credentials and cross-origin requests
  • 🔐 Bearer Token Support - Manual or automatic token handling
  • 🔄 Auto Retry - Adaptive backoff with exponential delay
  • ⚡ Built-in Features - Logging, timeout, and error handling
  • 🛡️ TypeScript Support - Full type safety and IntelliSense
  • 🌐 Cross-Origin Ready - Built-in credentials support
  • 🔒 Developer Control - You decide how to handle authentication

🚀 Quick Start

import { createClient } from 'hyperwiz';

// Simple client
const api = createClient('https://api.example.com', { logging: true });

// Make requests
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'John' });

📖 Usage Examples

Basic HTTP Methods

const api = createClient('https://api.example.com');

// GET request
const users = await api.get('/users');

// POST request
const newUser = await api.post('/users', { name: 'John', email: '[email protected]' });

// PUT request
const updatedUser = await api.put('/users/1', { name: 'John Updated' });

// PATCH request
const patchedUser = await api.patch('/users/1', { email: '[email protected]' });

// DELETE request
const deleted = await api.delete('/users/1');

// HEAD request - Get headers only (useful for checking if resource exists)
const userExists = await api.head('/users/123');

// OPTIONS request - Get allowed methods for a resource (CORS preflight)
const allowedMethods = await api.options('/users');

// PURGE request - Clear cache (useful for CDNs like Cloudflare)
const cacheCleared = await api.purge('/cache/clear');

Auto Retry with Adaptive Backoff

// Option 1: Simple boolean (uses default retry configuration)
const api1 = createClient('https://api.example.com', {
  retry: true, // Uses default: 3 retries, 1s delay, exponential backoff
  logging: true
});

// Option 2: Custom retry configuration
const api2 = createClient('https://api.example.com', {
  retry: {
    maxRetries: 5,           // Retry up to 5 times
    retryDelay: 500,         // Start with 500ms delay
    maxDelay: 5000,          // Maximum 5 second delay
    backoffMultiplier: 1.5,  // Gentler backoff (1.5x instead of 2x)
    retryOnStatus: [408, 429, 500, 502, 503, 504], // Retry on these status codes
    retryOnNetworkError: true // Retry on network errors
  },
  logging: true
});

// Option 3: Disable retry
const api3 = createClient('https://api.example.com', {
  retry: false, // No retry attempts
  logging: true
});

// All requests automatically retry if they fail
// Retries preserve the original HTTP method, headers, and body
const data = await api1.get('/unreliable-endpoint');
const user = await api2.post('/users', { name: 'John' }); // POST retries as POST

Request Caching (Smart In-Memory or IndexedDB)

// Option 1: Simple boolean (uses default cache configuration)
const api1 = createClient('https://api.example.com', {
  cache: true, // Uses default: 5min cache, memory storage, GET requests only
  logging: true
});

// Option 2: Custom cache configuration
const api2 = createClient('https://api.example.com', {
  cache: {
    enabled: true,
    maxAge: 10 * 60 * 1000,  // Cache for 10 minutes
    maxSize: 50,             // Maximum 50 cached items
    storage: 'indexeddb',    // Use IndexedDB for persistence
    includeQueryParams: true, // Include query params in cache key
    cacheableMethods: ['GET', 'HEAD'], // Cache GET and HEAD requests
    cacheableStatusCodes: [200, 304]   // Cache 200 and 304 responses
  },
  logging: true
});

// Option 3: Memory cache with custom settings
const api3 = createClient('https://api.example.com', {
  cache: {
    enabled: true,
    maxAge: 2 * 60 * 1000,   // Cache for 2 minutes
    maxSize: 20,             // Maximum 20 cached items
    storage: 'memory',       // Use in-memory storage (faster)
    includeQueryParams: false // Ignore query params for cache key
  },
  logging: true
});

// Option 4: Disable caching
const api4 = createClient('https://api.example.com', {
  cache: false, // No caching
  logging: true
});

// First request: fetches from server
const users1 = await api1.get('/users'); // ⏳ Network request

// Second request: served from cache (instant)
const users2 = await api1.get('/users'); // 💾 Cache hit (instant)

// Cache automatically expires after maxAge
// Cache automatically evicts old entries when maxSize is reached

Additional HTTP Methods

hyperwiz supports additional HTTP methods beyond the standard REST operations for enhanced functionality:

const api = createClient('https://api.example.com');

// HEAD - Get headers only (no body)
// Useful for checking if resource exists without downloading data
const userExists = await api.head('/users/123');
if (userExists.success) {
  console.log('User exists!');
  console.log('Last modified:', userExists.data.headers['last-modified']);
}

// OPTIONS - Get allowed methods for a resource
// Essential for CORS preflight requests and API discovery
const capabilities = await api.options('/users');
if (capabilities.success) {
  console.log('Allowed methods:', capabilities.data.headers['allow']);
  console.log('CORS headers:', capabilities.data.headers['access-control-allow-methods']);
}

// PURGE - Clear cache
// Useful for CDNs like Cloudflare, Fastly, etc.
const cacheCleared = await api.purge('/cache/clear');
if (cacheCleared.success) {
  console.log('Cache cleared successfully');
}

Use Cases:

  • HEAD: Check resource existence, get metadata, validate URLs
  • OPTIONS: CORS preflight, API discovery, check server capabilities
  • PURGE: Cache invalidation, CDN management, content updates

Automatic Content-Type Detection

hyperwiz automatically detects and sets the appropriate Content-Type header for common data types:

const api = createClient('https://api.example.com');

// 📊 JSON objects/arrays - automatically sets 'application/json'
const user = await api.post('/users', { name: 'John', email: '[email protected]' });
// Content-Type: application/json

// 📝 Plain text - automatically sets 'text/plain'
const message = await api.post('/messages', 'Hello, world!');
// Content-Type: text/plain

// 🌐 HTML content - automatically sets 'text/html'
const htmlContent = await api.post('/content', '<h1>Hello</h1><p>World</p>');
// Content-Type: text/html

// 📋 XML content - automatically sets 'application/xml'
const xmlData = await api.post('/data', '<?xml version="1.0"?><root><item>value</item></root>');
// Content-Type: application/xml

// 📦 FormData - automatically sets 'multipart/form-data'
const formData = new FormData();
formData.append('name', 'John');
formData.append('file', fileInput.files[0]);
const result = await api.post('/upload', formData);
// Content-Type: multipart/form-data

// 🔗 URLSearchParams - automatically sets 'application/x-www-form-urlencoded'
const params = new URLSearchParams();
params.append('name', 'John');
params.append('email', '[email protected]');
const response = await api.post('/users', params);
// Content-Type: application/x-www-form-urlencoded

// 📁 File/Blob objects - uses their built-in type property
await api.post('/upload', file); // Uses file.type
await api.post('/upload', blob); // Uses blob.type

// 💾 Binary data - defaults to 'application/octet-stream'
const buffer = new ArrayBuffer(8);
const binary = await api.post('/binary', buffer);
// Content-Type: application/octet-stream

// 📅 Dates - automatically converts to ISO string and sets 'application/json'
const date = new Date();
const event = await api.post('/events', date);
// Content-Type: application/json, body: "2024-01-01T00:00:00.000Z"

// 🔢 Numbers/Booleans - automatically sets 'application/json'
await api.post('/config', { enabled: true, count: 42 });
// Content-Type: application/json

Supported Data Types:

| Data Type | Content-Type | Detection Method | |-----------|-------------|------------------| | Objects/Arrays | application/json | Automatic | | Strings (JSON) | application/json | Content analysis | | Strings (HTML) | text/html | Content analysis | | Strings (XML) | application/xml | Content analysis | | Strings (other) | text/plain | Default | | FormData | multipart/form-data | Type check | | URLSearchParams | application/x-www-form-urlencoded | Type check | | File | file.type | File properties | | Blob | blob.type | Blob properties | | ArrayBuffer | application/octet-stream | Default | | TypedArrays | application/octet-stream | Default | | Date | application/json | Automatic | | Numbers/Booleans | application/json | Automatic |

Manual Override:

You can always manually override the Content-Type header:

// Override automatic detection
const response = await api.post('/custom', data, {
  'Content-Type': 'application/custom+json'
});

📁 File Upload Examples

Simple file upload with automatic Content-Type detection:

const api = createClient('https://api.example.com');

// 🖼️ Profile Picture Upload - Send actual file
const fileInput = document.getElementById('fileInput') as HTMLInputElement;
const file = fileInput.files[0];
await api.post('/profile/upload', file);
// Content-Type: image/jpeg (from file.type)

// 📄 Document Upload - Send actual file
const pdfFile = new File(['pdf content'], 'document.pdf', { type: 'application/pdf' });
await api.post('/documents/upload', pdfFile);
// Content-Type: application/pdf (from file.type)

// 🎵 Audio Upload - Send actual file
const audioBlob = new Blob(['audio content'], { type: 'audio/mpeg' });
await api.post('/audio/upload', audioBlob);
// Content-Type: audio/mpeg (from blob.type)

// 🎬 Video Upload - Send actual file
const videoFile = new File(['video content'], 'movie.mp4', { type: 'video/mp4' });
await api.post('/video/upload', videoFile);
// Content-Type: video/mp4 (from file.type)

// 📦 Multiple Files - Use FormData
const formData = new FormData();
formData.append('photo', photoFile);
formData.append('document', documentFile);
formData.append('audio', audioFile);
await api.post('/upload-multiple', formData);
// Content-Type: multipart/form-data

// 🖼️ Image Gallery Upload - Use FormData
const galleryForm = new FormData();
galleryFiles.forEach((file, index) => {
  galleryForm.append(`image${index}`, file);
});
await api.post('/gallery/upload', galleryForm);
// Content-Type: multipart/form-data

How it works:

  • Step 1: Developer sends actual file objects (File, Blob, FormData)
  • Step 2: Library uses the file's built-in type property
  • Step 3: Automatically sets the correct Content-Type header
  • Step 4: Sends the request with proper Content-Type
  • Result: Developer sends actual file content - zero configuration needed!
  • Supports: All file types that have proper MIME types

Manual Bearer Token Authentication

const api = createClient('https://api.example.com');

// Developer sends token manually in each request
const profile = await api.get('/user/profile', {
  'Authorization': 'Bearer your-token-here'
});

const newPost = await api.post('/posts', { title: 'Hello' }, {
  'Authorization': 'Bearer your-token-here'
});

Automatic Bearer Token with Interceptors

const api = createClient('https://api.example.com');

// Add token interceptor
api.addBefore((config, url) => {
  const token = localStorage.getItem('token');
  if (token) {
    return {
      ...config,
      headers: {
        ...config.headers,
        'Authorization': `Bearer ${token}`
      }
    };
  }
  return config;
});

// Login (no token yet - works correctly)
const login = await api.post('/login', { username: 'admin', password: '123' });

// Store token after successful login
if (login.success) {
  localStorage.setItem('token', login.data.token);
}

// Now all requests automatically include the token
const profile = await api.get('/user/profile');
const posts = await api.get('/user/posts');

Cookie-Based Authentication

// For session-based authentication with cookies
const api = createClient('http://localhost:4000', {
  credentials: 'include', // Important for cookies
  logging: true
});

// Login - server sets session cookie
const login = await api.post('/login', { username: 'admin' });

// Subsequent requests - cookies are automatically sent
const profile = await api.get('/user/profile');
const data = await api.get('/protected-data');

Custom Interceptors

const api = createClient('https://api.example.com');

// Request interceptor - runs before each request
api.addBefore((config, url) => {
  console.log(`🚀 Making ${config.method} request to ${url}`);
  return config;
});

// Response interceptor - runs after successful responses
api.addAfter((response, data, url) => {
  console.log(`✅ Response from ${url}:`, data);
  return data;
});

// Error interceptor - runs when errors occur (includes request config)
api.addErrorHandler((error, url, requestConfig) => {
  console.error(`❌ Error for ${requestConfig?.method} ${url}:`, error);
  return error;
});

Token Refresh Logic

const api = createClient('https://api.example.com');

api.addAfter((response, data, url) => {
  if (response.status === 401) {
    // Token expired, refresh it
    refreshToken();
  }
  return data;
});

// Or use error handler for more control
api.addErrorHandler((error, url, requestConfig) => {
  if (isAuthError(error)) {
    // Refresh token and retry the original request
    return handleAuthRetry(error, url, requestConfig);
  }
  return error;
});

Request Timeout

const api = createClient('https://api.example.com', {
  timeout: 5000 // 5 second timeout
});

// All requests will timeout after 5 seconds
const data = await api.get('/slow-endpoint');

⚙️ Configuration Options

const api = createClient('https://api.example.com', {
  logging: true,           // Enable request/response logging
  timeout: 30000,          // Request timeout in milliseconds
  credentials: 'include',  // Credentials mode for cross-origin
  retry: true,             // Auto retry (boolean) or retry config object
  // retry: {              // Custom retry configuration
  //   maxRetries: 3,      // Maximum retry attempts
  //   retryDelay: 1000,   // Initial delay in milliseconds
  //   maxDelay: 10000,    // Maximum delay in milliseconds
  //   backoffMultiplier: 2, // Exponential backoff multiplier
  //   retryOnStatus: [408, 429, 500, 502, 503, 504], // HTTP status codes to retry
  //   retryOnNetworkError: true // Retry on network errors
  // },
  cache: true,             // Request caching (boolean) or cache config object
  // cache: {              // Custom cache configuration
  //   enabled: true,      // Enable/disable caching
  //   maxAge: 300000,     // Cache duration in milliseconds (5 minutes)
  //   maxSize: 100,       // Maximum number of cached items
  //   storage: 'memory',  // Storage type: 'memory' or 'indexeddb'
  //   includeQueryParams: true, // Include query params in cache key
  //   cacheableMethods: ['GET', 'HEAD'], // HTTP methods that can be cached
  //   cacheableStatusCodes: [200] // Status codes that can be cached
  // },
      interceptors: {          // Custom interceptors
      before: [(config, url) => { /* ... */ }],
      after: [(response, data, url) => { /* ... */ }],
      onError: [(error, url, requestConfig) => { /* ... */ }]
    }
});

🔧 API Reference

Client Creation

// Basic client
createClient(baseUrl: string, config?: ClientConfig): HttpClient

// Public client (with logging enabled by default)
createPublicClient(baseUrl: string): HttpClient

createPublicClient - Quick Development Setup

The createPublicClient function is a convenience method that creates an HTTP client with logging enabled by default. Perfect for development and debugging.

import { createPublicClient } from 'hyperwiz';

// Creates a client with automatic logging
const api = createPublicClient('https://api.example.com');

// Equivalent to:
// const api = createClient('https://api.example.com', { logging: true });

// All requests are automatically logged to console
const users = await api.get('/users');
// Console output: ✅ 200 https://api.example.com/users [response data]

const newUser = await api.post('/users', { name: 'John' });
// Console output: ✅ 201 https://api.example.com/users [created user data]

Memory Management Functions

For long-running applications, hyperwiz provides memory management utilities to prevent memory leaks and monitor resource usage:

import { 
  cleanupCircuitBreakers,
  cleanupRetryAttempts, 
  cleanupPendingRequests,
  getMemoryStats 
} from 'hyperwiz';

cleanupCircuitBreakers()

Manually cleans up circuit breaker states that are older than 1 hour.

// Clean up circuit breaker tracking data
cleanupCircuitBreakers();

// Use in maintenance routines
setInterval(() => {
  cleanupCircuitBreakers();
  console.log('Circuit breakers cleaned up');
}, 30 * 60 * 1000); // Every 30 minutes

cleanupRetryAttempts()

Manually cleans up retry attempt tracking data older than 30 minutes.

// Clean up retry tracking data
cleanupRetryAttempts();

// Use during user session changes
function logout() {
  cleanupRetryAttempts(); // Clear retry history
  // ... other logout logic
}

cleanupPendingRequests()

Immediately clears all pending request deduplication data.

// Clear all pending requests (useful during app shutdown)
cleanupPendingRequests();

// Use when switching contexts or during cleanup
function resetApplication() {
  cleanupPendingRequests(); // Clear any ongoing requests
  // ... other reset logic
}

getMemoryStats()

Returns statistics about memory usage for monitoring and debugging.

// Monitor memory usage
const stats = getMemoryStats();
console.log('Memory stats:', stats);
// Output: {
//   circuitBreakers: 5,        // Number of active circuit breakers
//   retryAttempts: 12,         // Number of tracked retry attempts
//   pendingRequests: 3,        // Number of pending requests
//   globalCacheStorages: 2     // Number of cache storage instances
// }

// Use in monitoring dashboards
function monitorMemoryUsage() {
  const stats = getMemoryStats();
  
  if (stats.pendingRequests > 50) {
    console.warn('High number of pending requests:', stats.pendingRequests);
  }
  
  if (stats.circuitBreakers > 20) {
    console.warn('Many circuit breakers active, consider cleanup');
  }
  
  // Send to analytics or monitoring service
  analytics.track('hyperwiz_memory_stats', stats);
}

Complete Memory Management Example

import { 
  createClient, 
  cleanupCircuitBreakers, 
  cleanupRetryAttempts, 
  cleanupPendingRequests,
  getMemoryStats 
} from 'hyperwiz';

class ApiService {
  private api = createClient('https://api.example.com', {
    retry: true,
    cache: true,
    logging: process.env.NODE_ENV === 'development'
  });

  constructor() {
    this.startMaintenanceTimer();
  }

  // Periodic cleanup for memory management
  private startMaintenanceTimer() {
    setInterval(() => {
      // Clean up old data
      cleanupCircuitBreakers();
      cleanupRetryAttempts();
      
      // Monitor memory usage
      const stats = getMemoryStats();
      console.log('Memory cleanup completed:', stats);
      
      // Alert if memory usage is high
      if (stats.pendingRequests > 100) {
        console.warn('Memory leak warning: Too many pending requests');
      }
    }, 10 * 60 * 1000); // Every 10 minutes
  }

  // Clean shutdown
  async shutdown() {
    console.log('Shutting down API service...');
    
    // Force clear all pending requests
    cleanupPendingRequests();
    
    // Clean up tracking data
    cleanupCircuitBreakers();
    cleanupRetryAttempts();
    
    const finalStats = getMemoryStats();
    console.log('Final memory stats after cleanup:', finalStats);
  }

  // User logout - clear sensitive data
  async logout() {
    // Clear pending requests for security
    cleanupPendingRequests();
    console.log('User session cleaned up');
  }
}

// Usage
const apiService = new ApiService();

// During app shutdown
window.addEventListener('beforeunload', () => {
  apiService.shutdown();
});

Note: The library automatically runs cleanup every 10 minutes, but these manual functions give you fine-grained control for specific use cases like application shutdown, user logout, or custom maintenance schedules.

HTTP Methods

// All methods return Promise<ApiResponse<T>>
api.get<T>(url: string, headers?: HeadersInit)
api.post<T>(url: string, body: unknown, headers?: HeadersInit)
api.put<T>(url: string, body: unknown, headers?: HeadersInit)
api.patch<T>(url: string, body: unknown, headers?: HeadersInit)
api.delete<T>(url: string, headers?: HeadersInit)

// Additional HTTP methods
api.head<T>(url: string, headers?: HeadersInit)
api.options<T>(url: string, headers?: HeadersInit)
api.purge<T>(url: string, headers?: HeadersInit)

Interceptor Methods

// Add interceptors
api.addBefore(handler: RequestHandler)
api.addAfter(handler: ResponseHandler)
api.addErrorHandler(handler: ErrorHandler)

// Remove interceptors
api.removeBefore(handler: RequestHandler)
api.removeAfter(handler: ResponseHandler)
api.removeErrorHandler(handler: ErrorHandler)

// Clear all interceptors
api.clearInterceptors()

// Set timeout
api.setTimeout(timeoutMs: number)

// Cancel all requests
api.cancelAllRequests()

🎯 How Developers Use This Library

1. Simple API Client - Most Common Use Case

import { createClient } from 'hyperwiz';

// Create a simple API client
const api = createClient('https://api.example.com', {
  logging: true,  // See all requests in console
  retry: true     // Auto-retry failed requests
});

// Use it like fetch but simpler
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'John' });
const updatedUser = await api.put('/users/1', { name: 'John Updated' });
const deleted = await api.delete('/users/1');

2. Authentication with Bearer Tokens - Manual Control

const api = createClient('https://api.example.com');

// Developer manually adds tokens to each request
const profile = await api.get('/user/profile', {
  'Authorization': 'Bearer your-token-here'
});

const newPost = await api.post('/posts', { title: 'Hello' }, {
  'Authorization': 'Bearer your-token-here'
});

3. Automatic Token Injection - Using Interceptors

const api = createClient('https://api.example.com');

// Add token interceptor - automatically adds token to all requests
api.addBefore((config, url) => {
  const token = localStorage.getItem('token');
  if (token) {
    return {
      ...config,
      headers: {
        ...config.headers,
        'Authorization': `Bearer ${token}`
      }
    };
  }
  return config;
});

// Now all requests automatically include the token
const profile = await api.get('/user/profile'); // ✅ Token added automatically
const posts = await api.post('/posts', { title: 'New Post' }); // ✅ Token added automatically

4. Session-Based Authentication - Cookies

// For server-side sessions (like Express.js sessions)
const api = createClient('http://localhost:4000', {
  credentials: 'include', // Important: sends cookies automatically
  logging: true
});

// Login - server sets session cookie
const login = await api.post('/login', { username: 'admin', password: '123' });

// All subsequent requests automatically include the session cookie
const profile = await api.get('/user/profile'); // ✅ Cookie sent automatically
const data = await api.get('/protected-data'); // ✅ Cookie sent automatically

5. Reliable API with Auto-Retry - Production Ready

const api = createClient('https://api.example.com', {
  retry: {
    maxRetries: 3,           // Retry failed requests up to 3 times
    retryDelay: 1000,        // Start with 1 second delay
    maxDelay: 10000,         // Maximum 10 second delay
    backoffMultiplier: 2,    // Double the delay each retry
    retryOnStatus: [408, 429, 500, 502, 503, 504], // Retry on these errors
    retryOnNetworkError: true // Retry on network failures
  },
  timeout: 10000,            // 10 second timeout
  logging: true              // Log all requests and retries
});

// This request will automatically retry if it fails
// Retries preserve the original method (POST), body, and headers
const result = await api.post('/orders', { 
  productId: 123, 
  quantity: 2 
});

6. React Hook Pattern - Modern React Apps

import { useState, useCallback } from 'react';
import { createClient } from 'hyperwiz';

function useApi() {
  const [api] = useState(() => createClient('https://api.example.com', {
    retry: true,
    logging: process.env.NODE_ENV === 'development'
  }));

  const login = useCallback(async (credentials: any) => {
    const response = await api.post('/auth/login', credentials);
    if (response.success) {
      localStorage.setItem('auth-token', response.data.token);
    }
    return response;
  }, [api]);

  const logout = useCallback(() => {
    localStorage.removeItem('auth-token');
  }, []);

  const getProfile = useCallback(async () => {
    const token = localStorage.getItem('auth-token');
    return await api.get('/user/profile', {
      'Authorization': `Bearer ${token}`
    });
  }, [api]);

  return { api, login, logout, getProfile };
}

// Usage in component
function ProfileComponent() {
  const { getProfile } = useApi();
  
  const handleLoadProfile = async () => {
    const profile = await getProfile();
    if (profile.success) {
      console.log('Profile:', profile.data);
    }
  };
}

7. API Service Class - Organized Code

class UserAPI {
  private api = createClient('https://api.example.com', { 
    retry: true,
    logging: true 
  });

  constructor() {
    // Add authentication interceptor
    this.api.addBefore((config, url) => {
      const token = localStorage.getItem('token');
      if (token) {
        config.headers = { 
          ...config.headers, 
          'Authorization': `Bearer ${token}` 
        };
      }
      return config;
    });

    // Add error logging
    this.api.addErrorHandler((error, url, requestConfig) => {
      console.error(`API Error: ${requestConfig?.method} ${url}`, error);
      return error;
    });
  }

  async getUsers() {
    const response = await this.api.get('/users');
    return response.success ? response.data : [];
  }

  async createUser(userData: any) {
    const response = await this.api.post('/users', userData);
    return response.success ? response.data : null;
  }

  async updateUser(id: string, userData: any) {
    const response = await this.api.put(`/users/${id}`, userData);
    return response.success ? response.data : null;
  }

  async deleteUser(id: string) {
    const response = await this.api.delete(`/users/${id}`);
    return response.success;
  }
}

// Usage
const userAPI = new UserAPI();
const users = await userAPI.getUsers();
const newUser = await userAPI.createUser({ name: 'John', email: '[email protected]' });

8. E-commerce with Different Retry Strategies

class EcommerceAPI {
  // Critical operations (payments, orders) - aggressive retry
  private criticalApi = createClient('https://api.shop.com', {
    retry: {
      maxRetries: 5,           // More retries for critical operations
      retryDelay: 500,         // Start with shorter delay
      maxDelay: 5000,          // Shorter max delay
      backoffMultiplier: 1.5,  // Gentler backoff
      retryOnStatus: [500, 502, 503, 504], // Only retry server errors
      retryOnNetworkError: true
    }
  });

  // Normal operations (products, reviews) - standard retry
  private normalApi = createClient('https://api.shop.com', {
    retry: {
      maxRetries: 3,           // Standard retries
      retryDelay: 1000,        // Standard delay
      maxDelay: 10000,         // Standard max delay
      backoffMultiplier: 2,    // Standard backoff
      retryOnStatus: [408, 429, 500, 502, 503, 504],
      retryOnNetworkError: true
    }
  });

  // Critical operation - aggressive retry
  async createOrder(orderData: any) {
    return await this.criticalApi.post('/orders', orderData);
  }

  // Normal operation - standard retry
  async getProducts() {
    return await this.normalApi.get('/products');
  }

  async getProductReviews(productId: string) {
    return await this.normalApi.get(`/products/${productId}/reviews`);
  }
}

9. Custom Error Handling with Retry

const api = createClient('https://api.example.com', {
  retry: true,
  logging: true
});

// Custom error handling
api.addErrorHandler((error, url, requestConfig) => {
  // Log specific errors
  if (requestConfig?.method === 'POST' && url.includes('/orders')) {
    console.error('Order creation failed:', error);
  }
  
  // Handle specific error types
  if (isRateLimitError(error)) {
    console.warn('Rate limit hit, will retry with backoff');
  }
  
  return error; // Let the retry mechanism handle it
});

function isRateLimitError(error: unknown): boolean {
  return typeof error === 'object' && error !== null && 
         'status' in error && (error as any).status === 429;
}

10. Smart Caching for Performance

// E-commerce app with different caching strategies
class ProductAPI {
  // Product catalog - long cache (rarely changes)
  private catalogApi = createClient('https://api.shop.com', {
    cache: {
      enabled: true,
      maxAge: 30 * 60 * 1000,  // 30 minutes
      maxSize: 100,
      storage: 'indexeddb',     // Persistent across sessions
      cacheableMethods: ['GET', 'HEAD'],
      cacheableStatusCodes: [200]
    }
  });

  // User-specific data - short cache (changes frequently)
  private userApi = createClient('https://api.shop.com', {
    cache: {
      enabled: true,
      maxAge: 2 * 60 * 1000,   // 2 minutes
      maxSize: 20,
      storage: 'memory',        // Fast access
      cacheableMethods: ['GET', 'HEAD'],
      cacheableStatusCodes: [200]
    }
  });

  // Real-time data - no cache
  private realtimeApi = createClient('https://api.shop.com', {
    cache: false, // Always fresh data
    retry: true
  });

  // Product catalog (cached for 30 minutes)
  async getProducts(category?: string) {
    const url = category ? `/products?category=${category}` : '/products';
    return await this.catalogApi.get(url);
  }

  // User cart (cached for 2 minutes)
  async getUserCart() {
    return await this.userApi.get('/user/cart', {
      'Authorization': `Bearer ${localStorage.getItem('token')}`
    });
  }

  // Stock levels (no cache, always fresh)
  async getStockLevels(productId: string) {
    return await this.realtimeApi.get(`/products/${productId}/stock`);
  }

  // Clear cache when user logs out
  clearUserCache() {
    // Note: In a real app, you'd want to clear specific cache entries
    // This is a simplified example
    console.log('User logged out, cache will expire naturally');
  }
}

// Usage
const productAPI = new ProductAPI();

// First load: fetches from server
const products = await productAPI.getProducts('electronics'); // ⏳ Network request

// Subsequent loads: instant from cache
const products2 = await productAPI.getProducts('electronics'); // 💾 Cache hit (instant)

// User cart: cached briefly for performance
const cart = await productAPI.getUserCart(); // ⏳ Network request
const cart2 = await productAPI.getUserCart(); // 💾 Cache hit (instant)

// Stock levels: always fresh
const stock = await productAPI.getStockLevels('123'); // ⏳ Always network request

12. Advanced Caching with Cache Management

import { createClient } from 'hyperwiz';

// Create client with custom cache storage
const api = createClient('https://api.example.com', {
  cache: {
    enabled: true,
    maxAge: 5 * 60 * 1000,  // 5 minutes
    maxSize: 50,
    storage: 'indexeddb',    // Persistent storage
    includeQueryParams: true,
    cacheableMethods: ['GET', 'HEAD'],
    cacheableStatusCodes: [200]
  },
  logging: true
});

// Cache management functions
class CacheManager {
  static async clearCache() {
    // Clear all cached responses
    const cacheStorage = new IndexedDBCacheStorage();
    await cacheStorage.clear();
    console.log('Cache cleared');
  }

  static async getCacheStats() {
    const cacheStorage = new IndexedDBCacheStorage();
    const keys = await cacheStorage.keys();
    console.log(`Cache contains ${keys.length} items:`, keys);
    return keys;
  }

  static async removeExpiredCache() {
    const cacheStorage = new IndexedDBCacheStorage();
    const keys = await cacheStorage.keys();
    const now = Date.now();
    
    for (const key of keys) {
      const cached = await cacheStorage.get(key);
      if (cached && (now - cached.timestamp) > 5 * 60 * 1000) {
        await cacheStorage.delete(key);
        console.log(`Removed expired cache: ${key}`);
      }
    }
  }
}

// Usage
const users = await api.get('/users'); // First request: network
const users2 = await api.get('/users'); // Second request: cache

// Manage cache
await CacheManager.getCacheStats(); // See what's cached
await CacheManager.removeExpiredCache(); // Clean up expired entries
await CacheManager.clearCache(); // Clear all cache

🎯 Real-World Examples

React Hook with Token Management

import { useState } from 'react';
import { createClient } from 'hyperwiz';

function useApi() {
  const [api] = useState(() => createClient('https://api.example.com'));

  const login = async (credentials: any) => {
    const response = await api.post('/auth/login', credentials);
    if (response.success) {
      localStorage.setItem('auth-token', response.data.token);
    }
    return response;
  };

  const logout = () => {
    localStorage.removeItem('auth-token');
  };

  const makeAuthenticatedRequest = async (url: string, token: string) => {
    return await api.get(url, {
      'Authorization': `Bearer ${token}`
    });
  };

  return { api, login, logout, makeAuthenticatedRequest };
}

API Service Class

class UserAPI {
  private api = createClient('https://api.example.com', { logging: true });

  constructor() {
    // Add authentication interceptor
    this.api.addBefore((config, url) => {
      const token = localStorage.getItem('token');
      if (token) {
        config.headers = { ...config.headers, 'Authorization': `Bearer ${token}` };
      }
      return config;
    });
  }

  async getUsers() {
    const response = await this.api.get('/users');
    return response.success ? response.data : [];
  }

  async createUser(userData: any) {
    const response = await this.api.post('/users', userData);
    return response.success ? response.data : null;
  }

  async updateUser(id: string, userData: any) {
    const response = await this.api.put(`/users/${id}`, userData);
    return response.success ? response.data : null;
  }
}

E-commerce with Retry Strategies