Skip to main content

API Provider Architecture

Extensible, resilient API provider system for connecting to Hoosat blockchain nodes with automatic failover, load balancing, and performance optimization.

Overview

The API Provider Architecture introduces a flexible, pluggable system for connecting to Hoosat nodes. Instead of being tied to a single API endpoint, you can:

  • Use multiple providers with automatic failover
  • Optimize performance with fastest-response routing
  • Distribute load across multiple endpoints
  • Build custom providers for specialized use cases
  • Maintain 100% backward compatibility with existing code

This architecture was contributed by @codecustard in commit f922e47.

Core Concepts

ApiProvider Interface

All providers implement the ApiProvider interface, defining the contract for blockchain data access:

interface ApiProvider {
// Balance queries
getBalance(address: string): Promise<number>;

// UTXO fetching
getUtxos(addresses: string[]): Promise<UtxoForSigning[]>;

// Transaction submission
submitTransaction(transaction: Transaction): Promise<string>;

// Network information
getNetworkInfo(): Promise<NetworkInfo>;

// Fee recommendations
getFeeEstimate(): Promise<FeeEstimate>;

// Health check
ping(): Promise<boolean>;
}

This abstraction allows seamless provider swapping without changing application code.

Provider Types

1. HoosatProxyProvider

Connects to Hoosat proxy API (proxy.hoosat.net).

Features:

  • Official Hoosat proxy endpoint
  • Mainnet and testnet support
  • Full UTXO index support
  • Rate limiting protection

Example:

import { createHoosatProxyProvider } from 'hoosat-sdk-web';

const provider = createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1',
timeout: 10000,
debug: false
});

// Use provider
const balance = await provider.getBalance('hoosat:qz7ulu...');
console.log('Balance:', balance, 'sompi');

2. HoosatNetworkProvider

Connects to community network API (network.hoosat.fi).

Features:

  • Community-operated endpoint
  • Geographic distribution
  • Alternative to official proxy
  • Same API surface

Example:

import { createHoosatNetworkProvider } from 'hoosat-sdk-web';

const provider = createHoosatNetworkProvider({
baseUrl: 'https://network.hoosat.fi/api/v1',
timeout: 10000,
headers: {
'X-Custom-Header': 'value'
}
});

const utxos = await provider.getUtxos(['hoosat:qz7ulu...']);
console.log('Found', utxos.length, 'UTXOs');

3. MultiProvider

Composite provider that uses multiple backends with configurable strategies.

Strategies:

Failover Strategy

Primary provider with automatic fallback to backups on failure.

import {
createMultiProvider,
createHoosatProxyProvider,
createHoosatNetworkProvider
} from 'hoosat-sdk-web';

const provider = createMultiProvider(
[
createHoosatProxyProvider(), // Primary
createHoosatNetworkProvider() // Backup
],
'failover'
);

// Always tries proxy.hoosat.net first
// Falls back to network.hoosat.fi on error
const balance = await provider.getBalance(address);

Use cases:

  • Production wallets requiring maximum uptime
  • Critical payment systems
  • Services that need guaranteed availability
Fastest Strategy

Race all providers, use the fastest response.

const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider(),
customProvider
],
'fastest'
);

// Queries all three providers simultaneously
// Returns result from whichever responds first
const utxos = await provider.getUtxos([address]);

Use cases:

  • Latency-sensitive applications
  • Real-time payment verification
  • Global services with distributed users

Performance characteristics:

Single provider:     Response time = T1
Fastest strategy: Response time = min(T1, T2, T3)
Improvement: ~30-50% faster on average
Trade-off: More network requests
Round-Robin Strategy

Distribute requests evenly across providers.

const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider()
],
'round-robin'
);

// Request 1 → proxy.hoosat.net
// Request 2 → network.hoosat.fi
// Request 3 → proxy.hoosat.net
// Request 4 → network.hoosat.fi
// ...

Use cases:

  • Load distribution across multiple nodes
  • Avoiding rate limits on single endpoint
  • Fair usage of community resources

Benefits:

  • No single point of failure
  • Balanced load across infrastructure
  • Reduced risk of rate limiting

Practical Examples

Web Wallet with High Availability

import {
HoosatWebClient,
HoosatBrowserClient,
createMultiProvider,
createHoosatProxyProvider,
createHoosatNetworkProvider
} from 'hoosat-sdk-web';

// Create resilient multi-provider
const provider = createMultiProvider(
[
createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1',
timeout: 5000
}),
createHoosatNetworkProvider({
baseUrl: 'https://network.hoosat.fi/api/v1',
timeout: 5000
})
],
'failover'
);

// Create client with failover provider
const client = new HoosatBrowserClient(provider);

// Now all operations have automatic failover
const balance = await client.getBalance(wallet.address);
const utxos = await client.getUtxos([wallet.address]);
const txId = await client.submitTransaction(signedTx);

Performance-Optimized Payment Gateway

import { createMultiProvider, HoosatBrowserClient } from 'hoosat-sdk-web';

class PaymentGateway {
private client: HoosatBrowserClient;

constructor() {
// Use fastest strategy for minimum latency
const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider(),
this.createCustomProvider('https://my-node.example.com/api/v1')
],
'fastest'
);

this.client = new HoosatBrowserClient(provider);
}

async verifyPayment(
merchantAddress: string,
expectedAmount: string,
txId: string
): Promise<boolean> {
// Fastest provider responds
// Critical for user experience
const utxos = await this.client.getUtxos([merchantAddress]);

// Verify payment in UTXOs
const payment = utxos.find(utxo =>
utxo.outpoint.transactionId === txId &&
utxo.utxoEntry.amount === expectedAmount
);

return !!payment;
}

private createCustomProvider(baseUrl: string) {
return createHoosatProxyProvider({ baseUrl });
}
}

Load-Balanced Service

import { createMultiProvider, HoosatWebClient } from 'hoosat-sdk-web';

// Distribute load across multiple public endpoints
const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider(),
createHoosatProxyProvider({
baseUrl: 'https://backup.hoosat.net/api/v1'
})
],
'round-robin'
);

const client = new HoosatWebClient(provider);

// Load distributed evenly
// No single endpoint overwhelmed
for (let i = 0; i < 100; i++) {
const balance = await client.getBalance(addresses[i]);
// Requests spread: 33 → proxy, 33 → network, 34 → backup
}

Creating Custom Providers

Implement ApiProvider interface for specialized use cases:

import { ApiProvider, ProviderConfig } from 'hoosat-sdk-web';

class MyCustomProvider implements ApiProvider {
private config: ProviderConfig;

constructor(config: ProviderConfig) {
this.config = config;
}

async getBalance(address: string): Promise<number> {
const response = await fetch(
`${this.config.baseUrl}/addresses/${address}/balance`,
{
headers: this.config.headers,
signal: AbortSignal.timeout(this.config.timeout || 10000)
}
);

const data = await response.json();
return parseInt(data.balance);
}

async getUtxos(addresses: string[]): Promise<UtxoForSigning[]> {
const response = await fetch(
`${this.config.baseUrl}/addresses/utxos`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
...this.config.headers
},
body: JSON.stringify({ addresses }),
signal: AbortSignal.timeout(this.config.timeout || 10000)
}
);

const data = await response.json();
return data.utxos;
}

async submitTransaction(transaction: Transaction): Promise<string> {
// Implement transaction submission
// ...
}

async getNetworkInfo(): Promise<NetworkInfo> {
// Implement network info
// ...
}

async getFeeEstimate(): Promise<FeeEstimate> {
// Implement fee estimation
// ...
}

async ping(): Promise<boolean> {
try {
await fetch(`${this.config.baseUrl}/health`, {
signal: AbortSignal.timeout(2000)
});
return true;
} catch {
return false;
}
}
}

// Use custom provider
const customProvider = new MyCustomProvider({
baseUrl: 'https://my-api.example.com',
timeout: 10000,
debug: true
});

const client = new HoosatBrowserClient(customProvider);

Backward Compatibility

All existing code continues to work without changes:

// Old way (still works)
const client = new HoosatWebClient({
baseUrl: 'https://proxy.hoosat.net/api/v1'
});

// New way (with providers)
const provider = createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1'
});
const client = new HoosatBrowserClient(provider);

// Both produce identical behavior
const balance1 = await client.getBalance(address);
const balance2 = await client.getBalance(address);

Configuration Options

ProviderConfig

interface ProviderConfig {
// API endpoint
baseUrl: string;

// Request timeout (milliseconds)
timeout?: number; // Default: 10000

// Custom HTTP headers
headers?: Record<string, string>;

// Enable debug logging
debug?: boolean; // Default: false
}

EndpointConfig

interface EndpointConfig {
// Endpoint paths (optional overrides)
balanceEndpoint?: string; // Default: '/addresses/{address}/balance'
utxosEndpoint?: string; // Default: '/addresses/utxos'
submitEndpoint?: string; // Default: '/transactions'
networkInfoEndpoint?: string; // Default: '/info'
feeEstimateEndpoint?: string; // Default: '/fee/estimate'
}

Provider Health Monitoring

import { createMultiProvider, HoosatBrowserClient } from 'hoosat-sdk-web';

const providers = [
createHoosatProxyProvider(),
createHoosatNetworkProvider()
];

const multiProvider = createMultiProvider(providers, 'failover');

// Health check
async function checkProviderHealth() {
for (const provider of providers) {
const isHealthy = await provider.ping();
console.log(
`Provider ${provider.constructor.name}: ${isHealthy ? '✓' : '✗'}`
);
}
}

// Run periodic health checks
setInterval(checkProviderHealth, 30000); // Every 30 seconds

Error Handling

import { createMultiProvider, HoosatBrowserClient } from 'hoosat-sdk-web';

const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider()
],
'failover'
);

const client = new HoosatBrowserClient(provider);

try {
const balance = await client.getBalance(address);
console.log('Balance:', balance);
} catch (error) {
// All providers failed
console.error('All API providers unavailable:', error);

// Handle gracefully
showOfflineMessage();
}

Best Practices

1. Use Failover for Production

// Production wallet configuration
const provider = createMultiProvider(
[
createHoosatProxyProvider(), // Primary: official proxy
createHoosatNetworkProvider(), // Backup: community
customEnterpriseProvider // Last resort: your own node
],
'failover'
);

2. Use Fastest for Latency-Sensitive Apps

// Real-time payment verification
const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider()
],
'fastest'
);

3. Use Round-Robin for Load Distribution

// High-volume service
const provider = createMultiProvider(
[
createHoosatProxyProvider(),
createHoosatNetworkProvider(),
customProvider1,
customProvider2
],
'round-robin'
);

4. Configure Timeouts Appropriately

// Balance checks: fast timeout
const balanceProvider = createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1',
timeout: 3000 // 3 seconds
});

// Transaction submission: longer timeout
const txProvider = createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1',
timeout: 30000 // 30 seconds
});

5. Monitor Provider Performance

class MonitoredProvider implements ApiProvider {
private provider: ApiProvider;
private metrics: Map<string, number[]> = new Map();

constructor(provider: ApiProvider) {
this.provider = provider;
}

async getBalance(address: string): Promise<number> {
const start = Date.now();
try {
const result = await this.provider.getBalance(address);
this.recordMetric('getBalance', Date.now() - start);
return result;
} catch (error) {
this.recordMetric('getBalance', -1); // Error
throw error;
}
}

private recordMetric(method: string, duration: number) {
if (!this.metrics.has(method)) {
this.metrics.set(method, []);
}
this.metrics.get(method)!.push(duration);
}

getAverageLatency(method: string): number {
const durations = this.metrics.get(method) || [];
const successful = durations.filter(d => d >= 0);
return successful.reduce((a, b) => a + b, 0) / successful.length;
}
}

Migration Guide

From HoosatWebClient to Provider-Based

Before:

const client = new HoosatWebClient({
baseUrl: 'https://proxy.hoosat.net/api/v1'
});

After:

const provider = createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1'
});
const client = new HoosatBrowserClient(provider);

Adding Failover to Existing Code

Before:

const client = new HoosatWebClient({
baseUrl: 'https://proxy.hoosat.net/api/v1'
});

After:

const provider = createMultiProvider(
[
createHoosatProxyProvider({
baseUrl: 'https://proxy.hoosat.net/api/v1'
}),
createHoosatNetworkProvider()
],
'failover'
);
const client = new HoosatBrowserClient(provider);

No other code changes required - all method calls remain the same.

Next Steps