Package Exports
- reixo
- reixo/package.json
Readme
Reixo 🚀
A modern, type-safe HTTP client library with built-in resilience patterns, advanced queue management, and enterprise-grade features for Node.js and browsers.
✨ Features
- Builder Pattern - Fluent, chainable configuration API
- Automatic Retries - Exponential backoff with custom policies
- Task Queue - Concurrency control, priority, and dependency management
- Circuit Breaker - Fault tolerance and graceful degradation
- Progress Tracking - Real-time upload/download progress with event emitters
- Interceptor System - Modify requests/responses at multiple points
- Strict Typing - Full TypeScript support with zero
anytypes - Persistence - Offline queue support with multiple storage adapters
- Cross-Platform - Works in Node.js and modern browsers
- GraphQL Support - First-class GraphQL client wrapper
- Debugging Tools - Comprehensive error handling and logging
📦 Installation
npm install reixo
# or
yarn add reixo
# or
pnpm add reixo🚀 Quick Start
Basic Usage
import { Reixo } from 'reixo';
// Create a configured client
const client = Reixo.HTTPBuilder.create('https://jsonplaceholder.typicode.com')
.withTimeout(5000)
.withHeader('Accept', 'application/json')
.build();
// Make requests
const response = await client.get('/posts/1');
console.log(response.data);Advanced Configuration
const client = Reixo.HTTPBuilder.create('https://api.example.com')
.withRetry({
maxRetries: 3,
backoffFactor: 2,
initialDelayMs: 500,
retryCondition: (error) => error.status >= 500,
})
.withCircuitBreaker({
failureThreshold: 3,
resetTimeoutMs: 30000,
})
.withRateLimit({
requestsPerSecond: 10,
burstCapacity: 20,
})
.withCache({
ttl: 60000, // 1 minute
enabled: true,
})
.addRequestInterceptor((config) => {
// Add auth token to all requests
config.headers.set('Authorization', `Bearer ${getAuthToken()}`);
return config;
})
.addResponseInterceptor((response) => {
// Log successful responses
console.log(`Request to ${response.config.url} succeeded`);
return response;
})
.withDownloadProgress((progress) => {
console.log(`Download: ${progress.progress}%`);
})
.withUploadProgress((progress) => {
console.log(`Upload: ${progress.progress}%`);
})
.build();🔧 Core Features
HTTP Client
The main HTTP client supports all standard HTTP methods with full type safety:
// GET request
const posts = await client.get<Post[]>('/posts', {
params: { userId: 1 },
cache: { ttl: 30000 },
});
// POST request with typed body
const newPost = await client.post<Post>('/posts', {
title: 'My New Post',
body: 'This is the content',
userId: 1,
});
// PUT, PATCH, DELETE requests
await client.put('/posts/1', { title: 'Updated Title' });
await client.patch('/posts/1', { title: 'Patched Title' });
await client.delete('/posts/1');
// Custom requests
await client.request({
method: 'OPTIONS',
url: '/posts',
headers: { 'Custom-Header': 'value' },
});Resilience Patterns
Automatic Retries
// Standalone retry function
const result = await Reixo.withRetry(() => fetchUnreliableResource(), {
maxRetries: 5,
backoffFactor: 2,
initialDelayMs: 100,
retryCondition: (error) => error.status === 429 || error.status >= 500,
});
// Integrated with HTTP client
const client = Reixo.HTTPBuilder.create('https://api.example.com')
.withRetry({
maxRetries: 3,
backoffFactor: 1.5,
initialDelayMs: 200,
})
.build();Circuit Breaker
const breaker = new Reixo.CircuitBreaker({
failureThreshold: 3, // Open circuit after 3 failures
resetTimeoutMs: 30000, // Wait 30 seconds before half-open
onStateChange: (oldState, newState) => {
console.log(`Circuit changed from ${oldState} to ${newState}`);
},
});
// Execute with circuit breaker protection
try {
const result = await breaker.execute(() => client.get('/unstable-api'));
} catch (error) {
if (error.message.includes('Circuit is OPEN')) {
console.log('Service unavailable - using fallback');
// Provide fallback data
}
}Task Queue Management
const queue = new Reixo.TaskQueue({
concurrency: 3, // Max 3 concurrent tasks
autoStart: true, // Start processing immediately
persistent: true, // Persist queue to storage
storage: 'localStorage', // Storage adapter
});
// Add tasks with different priorities
queue.add(async () => await client.get('/high-priority'), {
priority: 100,
id: 'task-high',
dependencies: [],
});
queue.add(async () => await client.get('/medium-priority'), {
priority: 50,
id: 'task-medium',
dependencies: ['task-high'],
});
queue.add(async () => await client.get('/low-priority'), {
priority: 10,
id: 'task-low',
dependencies: ['task-medium'],
});
// Event handling
queue
.on('task:started', ({ id }) => console.log(`Task ${id} started`))
.on('task:completed', ({ id, result }) => console.log(`Task ${id} completed`))
.on('task:failed', ({ id, error }) => console.error(`Task ${id} failed:`, error))
.on('queue:empty', () => console.log('All tasks completed'));
// Queue control
queue.pause(); // Pause processing
queue.resume(); // Resume processing
queue.clear(); // Clear all tasks
await queue.waitUntilEmpty(); // Wait for all tasks to completePagination Helper
// Automatic pagination through API endpoints
for await (const page of Reixo.paginate(client, '/comments', {
pageParam: '_page',
limitParam: '_limit',
limit: 10,
initialPage: 1,
stopCondition: (response, pageItems, totalFetched) => totalFetched >= 50,
})) {
console.log(`Fetched ${page.length} items, total: ${totalFetched}`);
// Process page items
}GraphQL Client
const gqlClient = new Reixo.GraphQLClient('https://api.example.com/graphql', {
headers: { Authorization: 'Bearer token' },
});
// Query with variables
const result = await gqlClient.query({
query: `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`,
variables: { id: '1' },
});
// Mutation
const mutationResult = await gqlClient.mutate({
mutation: `
mutation CreateUser($input: UserInput!) {
createUser(input: $input) {
id
name
}
}
`,
variables: { input: { name: 'John Doe', email: 'john@example.com' } },
});Error Handling & Debugging
Reixo provides comprehensive error information:
try {
await client.get('/nonexistent');
} catch (error) {
if (error instanceof Reixo.HTTPError) {
console.log('HTTP Error Details:');
console.log('Status:', error.status); // 404
console.log('Status Text:', error.statusText); // 'Not Found'
console.log('URL:', error.config?.url); // '/nonexistent'
console.log('Method:', error.config?.method); // 'GET'
// Access response body
const errorBody = await error.response?.json();
console.log('Error Response:', errorBody);
// Access headers
console.log('Response Headers:', error.response?.headers);
} else {
console.log('Network Error:', error.message);
}
}Logging & Monitoring
// Custom logger
const logger = new Reixo.ConsoleLogger(Reixo.LogLevel.DEBUG);
const client = Reixo.HTTPBuilder.create('https://api.example.com')
.withLogger(logger)
.withMetrics({
enabled: true,
onMetricsUpdate: (metrics) => {
console.log('Request metrics:', metrics);
},
})
.build();📚 API Reference
HTTPBuilder
The main builder class for creating configured HTTP clients.
Reixo.HTTPBuilder.create(baseURL: string): HTTPBuilder
// Configuration methods
.withTimeout(timeoutMs: number): HTTPBuilder
.withHeader(name: string, value: string): HTTPBuilder
.withRetry(config: RetryConfig): HTTPBuilder
.withCircuitBreaker(config: CircuitBreakerConfig): HTTPBuilder
.withRateLimit(config: RateLimitConfig): HTTPBuilder
.withCache(config: CacheConfig): HTTPBuilder
.withLogger(logger: Logger): HTTPBuilder
.withMetrics(config: MetricsConfig): HTTPBuilder
// Interceptors
.addRequestInterceptor(interceptor: RequestInterceptor): HTTPBuilder
.addResponseInterceptor(interceptor: ResponseInterceptor): HTTPBuilder
.addErrorInterceptor(interceptor: ErrorInterceptor): HTTPBuilder
// Progress tracking
.withUploadProgress(callback: ProgressCallback): HTTPBuilder
.withDownloadProgress(callback: ProgressCallback): HTTPBuilder
// Final build
.build(): HTTPClientHTTPClient
The main client interface with all HTTP methods.
interface HTTPClient {
get<T = unknown>(url: string, options?: HTTPOptions): Promise<HTTPResponse<T>>;
post<T = unknown>(url: string, data?: unknown, options?: HTTPOptions): Promise<HTTPResponse<T>>;
put<T = unknown>(url: string, data?: unknown, options?: HTTPOptions): Promise<HTTPResponse<T>>;
patch<T = unknown>(url: string, data?: unknown, options?: HTTPOptions): Promise<HTTPResponse<T>>;
delete<T = unknown>(url: string, options?: HTTPOptions): Promise<HTTPResponse<T>>;
request<T = unknown>(config: HTTPRequestConfig): Promise<HTTPResponse<T>>;
// Advanced features
withCache(config: CacheConfig): HTTPClient;
withTransport(transport: Transport): HTTPClient;
}TaskQueue
Advanced task management with concurrency control.
class TaskQueue {
constructor(config?: TaskQueueConfig);
add(task: () => Promise<unknown>, options?: TaskOptions): string;
pause(): void;
resume(): void;
clear(): void;
waitUntilEmpty(): Promise<void>;
// Events
on(event: 'task:started', handler: (event: TaskEvent) => void): this;
on(event: 'task:completed', handler: (event: TaskCompletedEvent) => void): this;
on(event: 'task:failed', handler: (event: TaskFailedEvent) => void): this;
on(event: 'queue:empty', handler: () => void): this;
}🔍 Advanced Usage
Custom Storage Adapters
// Memory storage (default)
const memoryQueue = new Reixo.TaskQueue({
persistent: true,
storage: new Reixo.MemoryStorage(),
});
// LocalStorage (browser)
const localStorageQueue = new Reixo.TaskQueue({
persistent: true,
storage: new Reixo.LocalStorage('reixo-queue'),
});
// SessionStorage (browser)
const sessionStorageQueue = new Reixo.TaskQueue({
persistent: true,
storage: new Reixo.SessionStorage('reixo-queue'),
});
// Custom storage implementation
class CustomStorage implements StorageAdapter {
async save(key: string, data: unknown): Promise<void> {
/* implementation */
}
async load<T>(key: string): Promise<T | null> {
/* implementation */
}
async remove(key: string): Promise<void> {
/* implementation */
}
}Network Monitoring & Offline Support
// Monitor network status
const monitor = Reixo.NetworkMonitor.getInstance();
monitor.on('online', () => {
console.log('Network restored - resuming operations');
queue.resume();
});
monitor.on('offline', () => {
console.log('Network lost - pausing operations');
queue.pause();
});
// Start monitoring
monitor.start();
// Configure queue for offline support
const offlineQueue = new Reixo.TaskQueue({
concurrency: 2,
persistent: true,
storage: 'localStorage',
syncWithNetwork: true, // Auto-sync when network available
});Mocking for Testing
// Create mock adapter for testing
const mockAdapter = new Reixo.MockAdapter();
// Setup mock responses
mockAdapter
.onGet('/users/1')
.reply(200, { id: 1, name: 'John Doe' })
.onPost('/users')
.reply(201, (config) => ({
id: Math.random(),
...JSON.parse(config.data as string),
}))
.onAny()
.reply(404); // Catch-all for unmatched requests
// Create client with mock adapter
const testClient = Reixo.HTTPBuilder.create('https://api.example.com')
.withTransport(mockAdapter)
.build();
// Use in tests
const response = await testClient.get('/users/1');
expect(response.data).toEqual({ id: 1, name: 'John Doe' });🛠️ Configuration Options
Retry Configuration
interface RetryConfig {
maxRetries?: number; // Maximum retry attempts (default: 3)
backoffFactor?: number; // Multiplier for delay (default: 2)
initialDelayMs?: number; // Initial delay in ms (default: 100)
maxDelayMs?: number; // Maximum delay in ms (default: 30000)
retryCondition?: (error: HTTPError) => boolean; // Custom retry logic
}Circuit Breaker Configuration
interface CircuitBreakerConfig {
failureThreshold?: number; // Failures before opening (default: 5)
resetTimeoutMs?: number; // Time before half-open (default: 30000)
onStateChange?: (oldState: CircuitState, newState: CircuitState) => void;
}Task Queue Configuration
interface TaskQueueConfig {
concurrency?: number; // Maximum concurrent tasks (default: 1)
autoStart?: boolean; // Start processing immediately (default: true)
persistent?: boolean; // Persist queue to storage (default: false)
storage?: StorageAdapter; // Storage implementation
syncWithNetwork?: boolean; // Auto-sync with network status (default: false)
}🚨 Error Types
Reixo provides detailed error information:
// HTTPError - For HTTP status codes >= 400
class HTTPError extends Error {
status: number;
statusText: string;
config?: HTTPRequestConfig;
response?: HTTPResponse;
}
// NetworkError - For connectivity issues
class NetworkError extends Error {
originalError: Error;
}
// TimeoutError - For request timeouts
class TimeoutError extends Error {
timeoutMs: number;
}
// CircuitBreakerError - When circuit is open
class CircuitBreakerError extends Error {
state: CircuitState;
}📊 Performance Monitoring
// Enable metrics collection
const client = Reixo.HTTPBuilder.create('https://api.example.com')
.withMetrics({
enabled: true,
collectionWindowMs: 60000, // 1 minute window
onMetricsUpdate: (metrics) => {
console.log('Request Success Rate:', metrics.successRate);
console.log('Average Latency:', metrics.averageLatencyMs);
console.log('Total Requests:', metrics.totalRequests);
},
})
.build();
// Access metrics directly
const metrics = client.getMetrics();
console.log('95th Percentile Latency:', metrics.percentile95LatencyMs);🔒 Security Features
const secureClient = Reixo.HTTPBuilder.create('https://api.example.com')
.withSecurity({
sanitizeHeaders: true, // Remove sensitive headers from logs
maskSensitiveData: true, // Mask passwords/tokens in logs
ssl: {
rejectUnauthorized: true, // Strict SSL validation
minVersion: 'TLSv1.2', // Minimum TLS version
},
})
.build();🌐 Browser Support
Reixo works in all modern browsers:
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
For older browsers, include these polyfills:
<!-- For IE11 support -->
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js"></script>📦 Bundle Size
Reixo is optimized for minimal bundle impact:
- Core: ~15KB (gzipped)
- With all features: ~25KB (gzipped)
- Tree-shakable: Only include what you use
🚀 Migration Guide
From axios
// axios
import axios from 'axios';
const response = await axios.get('/api/data');
// Reixo
import { Reixo } from 'reixo';
const client = Reixo.HTTPBuilder.create('/api').build();
const response = await client.get('/data');From fetch
// fetch
const response = await fetch('/api/data');
const data = await response.json();
// Reixo
const response = await client.get('/data');
const data = response.data; // Already parsed JSON🛠️ Development
Building
npm run buildTesting
npm test # Run tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportLinting
npm run lint # Check code style
npm run lint:fix # Auto-fix issues🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
MIT License - see LICENSE for details.
🆘 Support
🎯 Examples
Check the /examples directory for comprehensive usage examples:
01-basic-requests.ts- Basic HTTP operations02-resilience-retry-circuit.ts- Retry and circuit breaker03-queue-offline-sync.ts- Task queue with offline support04-caching-pagination.ts- Caching and pagination05-graphql-client.ts- GraphQL operations06-interceptors-mocking.ts- Interceptors and testing07-logging-metrics.ts- Logging and performance monitoring08-error-handling.ts- Comprehensive error handling
Built with ❤️ by Your Name