Skip to content
/ voxa Public

A modern, feature-rich HTTP client built on Native Fetch API with advanced capabilities for enterprise applications.

License

Notifications You must be signed in to change notification settings

0xshariq/voxa

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Voxa HTTP Client

Modern, feature-rich HTTP client for Node.js and browsers, built on the native Fetch API. Modular architecture with separate feature packages for optimal bundle size.

npm version License: MIT


πŸ“Š Why Voxa?

Voxa isn't just another HTTP clientβ€”it's a complete request management system with a modular architecture. Install only the features you need, keeping your bundle size minimal while having access to advanced capabilities that Axios, Fetch, and other popular clients simply don't have.

🎯 Modular Design

Voxa is split into multiple packages:

  • @0xshariq/voxa-core (~140KB) - Essential HTTP client with caching, retry, queue, rate limiting, and deduplication
  • @0xshariq/voxa-streaming-images - Image streaming with progress tracking
  • @0xshariq/voxa-streaming-videos - Video streaming with progress tracking
  • @0xshariq/voxa-http2 - HTTP/2 Server Push support
  • @0xshariq/voxa-graphql - GraphQL query support
  • @0xshariq/voxa-batch - Request batching
  • @0xshariq/voxa-offline - Offline queue management
  • @0xshariq/voxa-circuit-breaker - Circuit breaker pattern
  • @0xshariq/voxa-token - OAuth/JWT token management
  • @0xshariq/voxa-metrics - Performance metrics tracking
  • @0xshariq/voxa-cancel - Advanced request cancellation

Future Plans

  • Voxa CLI (Under Development)
  • Voxa API (Private API only for trusted users and organizations) (Done)
  • Voxa SDKs (Go,Rust,Python and Ruby) (Not Planned Yet)

Feature Comparison

Feature Voxa Axios Fetch ky Got node-fetch
Bundle Size (Minified) 21 KB 32.1 KB 0KB 14.6 KB 180 KB 18 KB
Bundle Size (Gzipped) 5.8 KB 12.1 KB 0KB 4.8 KB 52 KB 6.5 KB
Dependencies 0 4 0 0 15 0
Modular Architecture βœ… ❌ ❌ ❌ ❌ ❌
TypeScript First βœ… βœ… βœ… βœ… βœ… ⚠️
Browser + Node.js βœ… βœ… βœ… βœ… ❌ ❌
Automatic Retry βœ… ❌ ❌ βœ… βœ… ❌
Response Caching βœ… Advanced ❌ ❌ βœ… Basic βœ… Advanced ❌
Request Deduplication βœ… ❌ ❌ ❌ ❌ ❌
Priority Queue βœ… ❌ ❌ ❌ ❌ ❌
Batch Requests βœ… ❌ ❌ ❌ ❌ ❌
Token Management βœ… Advanced ❌ ❌ ❌ ❌ ❌
Offline Queue βœ… ❌ ❌ ❌ ❌ ❌
GraphQL Support βœ… ❌ ❌ ❌ ❌ ❌
Streaming Progress βœ… Advanced βœ… Basic βœ… Basic βœ… βœ… βœ…
Request/Response Interceptors βœ… βœ… ❌ βœ… βœ… ❌
Circuit Breaker βœ… ❌ ❌ ❌ ❌ ❌
Rate Limiting βœ… ❌ ❌ ❌ ❌ ❌
SSRF Protection βœ… ❌ ❌ ❌ ❌ ❌
Debug Mode βœ… ⚠️ ❌ ⚠️ βœ… ❌
Cancel Requests βœ… βœ… βœ… βœ… βœ… βœ…
Schema Validation βœ… ❌ ❌ ❌ ❌ ❌
Error Classification βœ… ⚠️ ❌ ⚠️ βœ… ❌
Metadata Tracking βœ… ❌ ❌ ❌ ❌ ❌

✨ Core Features (in @0xshariq/voxa-core)

  • πŸ”„ Automatic Retry (exponential backoff)
  • πŸ’Ύ Response Caching (memory/file/custom)
  • 🎯 Request Deduplication
  • ⚑ Priority Queue
  • πŸ”’ TypeScript First
  • πŸ”Œ Interceptors
  • πŸ“Š Request Tracking & Metadata
  • ⏱️ Timeout Control
  • πŸš€ Static Methods
  • πŸ“‘ Schema Validation
  • πŸ” SSRF Protection
  • πŸ› Debug Mode
  • πŸ§ͺ Automatic JSON Parsing
  • βš–οΈ Rate Limiting

🎁 Optional Feature Packages

Install only what you need:

  • πŸ“Ή @0xshariq/voxa-streaming-images - Image upload/download with progress
  • 🎬 @0xshariq/voxa-streaming-videos - Video upload/download with progress
  • ⚑ @0xshariq/voxa-http2 - HTTP/2 Server Push
  • πŸ” @0xshariq/voxa-graphql - GraphQL queries
  • πŸ“¦ @0xshariq/voxa-batch - Request batching
  • πŸ“΄ @0xshariq/voxa-offline - Offline queue
  • πŸ”Œ @0xshariq/voxa-circuit-breaker - Circuit breaker pattern
  • 🎫 @0xshariq/voxa-token - OAuth/JWT token management
  • πŸ“Š @0xshariq/voxa-metrics - Performance metrics
  • πŸ›‘ @0xshariq/voxa-cancel - Advanced cancellation

πŸ“¦ Installation

Core Package (Required)

npm install @0xshariq/voxa-core
# or
pnpm install @0xshariq/voxa-core
# or
yarn add @0xshariq/voxa-core

Feature Packages (Optional)

# Install only the features you need
npm install @0xshariq/voxa-graphql @0xshariq/voxa-streaming-images

πŸš€ Quick Start

Basic Usage (Core Only)

import { Voxa } from "@0xshariq/voxa-core";

const client = new Voxa({
  baseURL: "https://api.example.com",
  timeout: 5000,
  cache: {
    enabled: true,
    ttl: 60000,
  },
  retry: {
    enabled: true,
    count: 3,
  },
});

const response = await client.get("/users");
console.log(response.data);

With Feature Packages

import { Voxa } from "@0xshariq/voxa-core";
import "@0xshariq/voxa-graphql"; // Types auto-merge
import "@0xshariq/voxa-batch";
import { StreamingImageManager } from "@0xshariq/voxa-streaming-images";

const client = new Voxa({
  baseURL: "https://api.example.com",
  graphql: {
    // TypeScript knows about this!
    enabled: true,
    endpoint: "/graphql",
  },
  batch: {
    enabled: true,
    wait: 100,
  },
});

// Use streaming
const imageManager = new StreamingImageManager();
await imageManager.upload("/upload", imageFile, {}, (sent, total) => {
  console.log(`Progress: ${((sent / total) * 100).toFixed(2)}%`);
});

Complete Instance Example

import { Voxa } from "@0xshariq/voxa-core";

const api = new Voxa({
  baseURL: "https://api.example.com",
  timeout: 5000,
  headers: {
    "Content-Type": "application/json", // string
    Authorization: "Bearer <token>", // string
  },
  priority: "high", // 'critical' | 'high' | 'normal' | 'low'
  retry: {
    enabled: true, // boolean (default: true)
    count: 5, // number (max: 5)
    delay: 1000, // number (ms)
    exponentialBackoff: true, // boolean
    maxRetry: 10000, // number (ms)
    statusCodes: [429, 500, 502, 503, 504], // number[]
  },
  deduplication: {
    enabled: true, // boolean (default: true)
    ttl: 300000, // number (ms)
  },
  cache: {
    enabled: true, // boolean (default: true)
    ttl: 300000, // number (ms, default: 5 min)
    storage: "memory", // 'memory' | 'custom'
    adapter: undefined, // custom cache adapter (optional)
  },
  queue: {
    enabled: true, // boolean
    maxConcurrent: 5, // number
  },
  batch: {
    enabled: true, // boolean
    endpoint: "/batch", // string (optional)
    wait: 100, // number (ms, optional)
    maxBatchSize: 10, // number (optional)
  },
  token: {
    enabled: true, // boolean
    type: "bearer", // 'bearer' | 'oauth2' | 'jwt'
    tokenEndpoint: "/auth/token", // string
    clientId: "client-id", // string
    clientSecret: "client-secret", // string
    refreshEndpoint: "/auth/refresh", // string
    storage: "memory", // 'memory' | 'localStorage'
    getToken: async () => "token", // function
    setToken: (token: string) => {}, // function
    refreshToken: async () => "new-token", // function
  },
  // Token refresh failure hook
  onTokenRefreshFailure: (error, context) => {
    // Custom logic: log, alert, or trigger re-authentication
    console.error("Token refresh failed:", error, context);
    // Optionally, redirect user or clear session
  },

  // Multi-tenant/multi-user support example
  getToken: async (userId) => {
    // Fetch token for a specific user/tenant
    return await fetchTokenForUser(userId);
  },
  setToken: (token, userId) => {
    // Store token for a specific user/tenant
    saveTokenForUser(token, userId);
  },
  refreshToken: async (userId) => {
    // Refresh token for a specific user/tenant
    return await refreshUserToken(userId);
  },

  // Usage:
  // await api.get('/resource', { userId: 'user-42' });
  offline: {
    enabled: true, // boolean (default: true)
    storage: "localStorage", // 'localStorage' | 'indexedDB'
  },
  circuitBreaker: {
    enabled: true, // boolean
    threshold: 5, // number
    timeout: 10000, // number (ms)
    onOpen: () => {}, // function (optional)
  },
  metrics: {
    enabled: true, // boolean
  },
  errors: {
    enabled: true, // boolean (default: true)
  },
  rate: {
    enabled: true, // boolean (default: true)
    maxRequests: 100, // number
    perMilliseconds: 60000, // number (ms)
  },
  schema: {
    enabled: true, // boolean
    requestSchema: undefined, // any (optional)
    responseSchema: undefined, // any (optional)
    library: "zod", // 'zod' | 'yup' (optional)
  },
  cancel: {
    enabled: true, // boolean (default: true)
  },
  graphql: {
    enabled: true, // boolean
    endpoint: "https://graphqlzero.almansi.me/api", // string
    logErrors: true, // boolean
    headers: undefined, // Record<string, string> (optional)
    timeout: undefined, // number (optional)
    cache: undefined, // boolean (optional)
  },
  interceptors: {
    request: [
      (config) => {
        /* modify config */ return config;
      },
    ],
    response: [
      (response) => {
        /* log/modify response */ return response;
      },
    ],
  },
  metadata: {
    enabled: true, // boolean (default: true)
    log: true, // boolean (log metadata events)
    fields: [
      "id",
      "method",
      "endpoint",
      "priority",
      "timestamp",
      "startTime",
      "endTime",
    ], // string[] (fields to track)
    maxEntries: 100, // number (max entries to keep)
    customHandler: (meta) => {}, // function (custom handler)
  },
});

πŸš€ Usage

🧩 Feature Modularity & Extensibility

Voxa's features (cache, retry, queue, batch, deduplication, interceptors, etc.) are fully modular and can be enabled, disabled, or extended at runtime.


Enabling/Disabling Features at Runtime

You can toggle features on/off by updating the instance configuration:

// Disable cache and queue at runtime
api.updateConfig({
  cache: { enabled: false },
  queue: { enabled: false },
});

// Enable retry and set new retry count
api.updateConfig({
  retry: { enabled: true, count: 10 },
});

Note: Not all features support dynamic reconfiguration in-flight. For critical changes, create a new instance with the desired config.


Extending with Plugins (Custom Features)

You can add your own features or override built-in ones by attaching custom managers or hooks:

// Example: Add a custom logging plugin
api.usePlugin({
  onRequest(config) {
    console.log("Request:", config.url);
    return config;
  },
  onResponse(response) {
    console.log("Response:", response.status);
    return response;
  },
});

Plugin Interface

A plugin is an object with any of these hooks:

  • onRequest(config)
  • onResponse(response)
  • onError(error)
  • onBatch(batch)
  • onCacheEvent(event)

You can register multiple plugins. They are called in the order added.


Disabling a Feature for a Single Request

// Disable cache for a single request
await api.get('/users', { cache: { enabled: false } });

// Set custom queue priority for a single request
await api.post('/orders', { ... }, { priority: 'critical' });

See Advanced Features for more on feature modularity and plugins.


Using Voxa with an Instance

Injecting Custom requestId & Metadata

RequestId will generate automatically and will use in features (like retry,batching,etc..):

// Provide a custom requestId and metadata for tracing
const response = await api.get("/users/1", {
  requestId: "trace-abc-123", // requestid will generate per request
  metadata: {
    traceId: "trace-abc-123",
    userId: "user-42",
    customField: "my-value",
  },
});
console.log(response.metadata); // includes your custom fields

Note: The requestId generates automatically and use in all features.

Create a client instance to reuse configuration and advanced features:

import voxa from "@0xshariq/voxa";

const api = voxa.create({
  baseURL: "https://api.example.com",
  timeout: 5000,
  // ...other options
});

// Make requests (response is always parsed JSON)
const response = await api.get<User>("/users/1");
console.log(response.data); // { id, name, ... }

// You can also use other HTTP methods:
await api.post("/users", { name: "John" });
await api.put("/users/1", { name: "Jane" });
await api.delete("/users/1");

Using Voxa Without an Instance (Static Methods)

Call static methods directly for one-off requests:

import { Voxa } from "@0xshariq/voxa";

const response = await Voxa.get<User>("https://api.example.com/users/1");
console.log(response.data);

// Other static methods:
await Voxa.post("https://api.example.com/users", { name: "John" });
await Voxa.put("https://api.example.com/users/1", { name: "Jane" });
await Voxa.delete("https://api.example.com/users/1");

Use an instance for advanced features (caching, queueing, interceptors, GraphQL, etc.), or static methods for simple requests.


TypeScript Generics

interface User {
  id: number;
  name: string;
  email: string;
}

// Type-safe responses
const response = await api.get<User>("/users/1");
const user: User = await response.json();

Streaming Upload/Download

import { StreamingImageManager, StreamingVideoManager } from "@0xshariq/voxa";

// Upload image with progress tracking
const streamingImages = new StreamingImageManager({});

const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

await streamingImages.upload(
  "https://api.example.com/images/upload",
  file,
  { "Content-Type": "image/jpeg" },
  (sentBytes, totalBytes) => {
    const percentage = (sentBytes / totalBytes) * 100;
    console.log(`Upload progress: ${percentage.toFixed(2)}%`);
  }
);

// Download video with progress
const streamingVideos = new StreamingVideoManager({});

const response = await streamingVideos.download(
  "https://api.example.com/videos/12345",
  {},
  (receivedBytes, totalBytes) => {
    console.log(`Downloaded: ${receivedBytes}/${totalBytes} bytes`);
  }
);

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

See Streaming Guide for complete examples and advanced usage.


πŸ“– Documentation

Core Documentation

Documentation:

See docs/ for full details and up-to-date usage.


🎯 Key Features

Automatic Retry with Exponential Backoff

const api = voxa.create({
  baseURL: "https://api.example.com",
  retry: {
    count: 3, // Max 3 retries
    delay: 1000, // Initial delay: 1s
    exponentialBackoff: true, // 1s β†’ 2s β†’ 4s
    maxRetry: 10000, // Max delay: 10s
  },
});

Response Structure

{
  response: Response, // Http Response object
  data: {}, // Api Data will store here
  metadata: {
    id: "string", // requestId used everywhere
    method: "'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'",
    endpoint: "string",
    priority: "'critical' | 'high' | 'normal' | 'low'",
    timestamp: 2000,
    startTime: 2000,
    endTime: 2000
  },
  requestId: string, // Unique requestId, used in all features (queue, batch, cache, metrics, etc.)
  status: 200,
  statusText: "Ok"
}

See Response Structure for detailed info.


Response Caching

// Memory cache (default)
const api = voxa.create({
  cache: {
    enabled: true,
    type: "memory",
    ttl: 300000, // 5 minutes
  },
});

// Redis cache
const api = voxa.create({
  cache: {
    enabled: true,
    type: "redis",
    ttl: 300000,
  },
});

Request Prioritization

// High priority request
await api.get("/critical-data", { priority: "critical" });

// Normal priority (default)
await api.get("/regular-data");

// Low priority
await api.get("/background-data", { priority: "low" });

Request Interceptors

// Add authentication header
api.interceptors.request.use((config) => {
  config.headers = {
    ...config.headers,
    Authorization: `Bearer ${getToken()}`,
  };
  return config;
});

// Log responses
api.interceptors.response.use((response) => {
  console.log("Response:", response.status);
  return response;
});

πŸ”§ Configuration

Environment Variables

Create a .env file in your project root:

# Cache Configuration (Generic - works with any cache backend)
CACHE_URL=redis://localhost:6379
CACHE_PASSWORD=your-password
CACHE_HOST=localhost
CACHE_PORT=6379
CACHE_DB=0

# HTTP Configuration
HTTP_TIMEOUT=5000
HTTP_BASE_URL=https://api.example.com

See Configuration Guide for detailed options.


πŸ“Š Monitoring & Statistics

// Get cache statistics
const cacheStats = api.getCacheStats();
console.log(cacheStats); // { storage: 'memory', size: 10, entries: [...] }

// Get queue statistics
const queueStats = api.getQueueStats();
console.log(queueStats); // { queueSize: 2, activeRequests: 3, maxConcurrent: 5 }

// Get request metadata
const metadata = api.getRequestMetadata("request-id-123");
console.log(metadata); // { id, method, endpoint, duration, ... }

Request ID Format and Expiry

Each request is assigned a unique requestId for tracking and feature management. The format is:

<timestamp>-<expiry>-<random>
  • timestamp: When the request was created

  • expiry: When the requestId expires (used for cache/batch conflict avoidance)

  • random: Random string for uniqueness

  • Cache feature uses a 5 minute expiry (default).

  • Batch feature uses a 15 minute expiry.

This prevents conflicts when the same request is sent at different times. Always use the generated requestId for all features (cache, batch, queue, etc.).


Error Classification & Debugging

Voxa now provides detailed error classification and debugging messages for every request. Use api.classifyError(error) to get both the error category and a helpful message for easier troubleshooting.


Feature Manager Access

All feature managers (cache, queue, deduplication, metadata, circuit breaker, batch, rate limiter, metrics, schema, error classifier) are accessible via public methods or stats getters:

  • api.getCacheStats()
  • api.getQueueStats()
  • api.getDeduplicationStats()
  • api.getMetadataStats()
  • api.circuitBreaker()
  • api.batch()
  • api.rate()
  • api.metrics()
  • api.schema()
  • api.classifyError(error)

πŸ§ͺ Testing

# Run test file directly
pnpm test

# Or using development mode
pnpm dev

πŸ“ HTTP Methods

Voxa supports all standard HTTP methods:

await api.get("/users");
await api.post("/users", { name: "John" });
await api.put("/users/1", { name: "Jane" });
await api.patch("/users/1", { email: "[email protected]" });
await api.delete("/users/1");
await api.head("/users");
await api.options("/users");

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

See the Contributing Guide


πŸ“„ License

MIT Β© Sharique Chaudhary


πŸ”— Links


Note: This project is built on the native Fetch API and requires Node.js 18+ or a modern browser environment.

About

A modern, feature-rich HTTP client built on Native Fetch API with advanced capabilities for enterprise applications.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published