@eric8810/catcher-napi-http
v0.3.10
Published
catcher HTTP native addon for Node.js via napi-rs
Readme
@eric8810/catcher-napi-http
Rust-powered HTTP + SSE client for Node.js via napi-rs. Part of the catcher toolkit.
Wraps catcher-http's HttpTransport — reqwest + retry + circuit breaker, compiled to a native addon. Includes typed TypeScript wrappers with auto-generated .d.ts.
⚠️ Breaking Changes (0.3.0+)
Migrate from 0.2.x → 0.3.x — see napi API docs for full details.
v0.3.2:
HttpResponse,RequestOptions,Metricstypes now correctly use camelCase (auto-generated by NAPI-RS). JSON config types (HttpClientConfig, etc.) remain snake_case.
| Change | Before | After |
|--------|--------|-------|
| Entry point | client.js / client.d.ts | dist/client.js / dist/client.d.ts |
| Config format | JSON.stringify(config) only | Typed object or JSON string |
| Class names | JsHttpClient, JsSseStream, JsSseClient | HttpClient, SseStream, SseClient |
| Callback events | Raw JSON strings, need JSON.parse() | Typed objects, auto-parsed |
| Default backoff | Exponential | Fixed |
| Default connect_timeout_ms | 5000 | 10000 |
| Default min_backoff_ms | 500 | 100 |
| Default max_backoff_ms | 30000 | 10000 |
| camelCase fields | Not supported | #[serde(alias)] — both snake_case and camelCase accepted |
- const client = require('@eric8810/catcher-napi-http').HttpClient
- const c = new HttpClient(JSON.stringify({ base_url: '...' }))
+ import { HttpClient } from '@eric8810/catcher-napi-http'
+ const c = new HttpClient({ base_url: '...' })Install
npm install @eric8810/catcher-napi-httpPre-built binaries available for Linux (x64/arm64 gnu/musl), macOS (x64/arm64), and Windows (x64/arm64). Installed automatically via platform-specific optionalDependencies.
Usage
import { HttpClient } from '@eric8810/catcher-napi-http'
import type { HttpClientConfig, SseEvent } from '@eric8810/catcher-napi-http/types'
// Config as typed object (recommended) or JSON string
const client = new HttpClient({
base_url: 'https://api.example.com',
connect_timeout_ms: 10000,
retry: { max_attempts: 3, backoff: 'Fixed' },
circuit_breaker: { failure_threshold: 5, reset_timeout_ms: 30000 },
dns: {
cache_size: 512,
cache_ttl_secs: 300,
negative_ttl_secs: 60,
stale_ttl_secs: 3600,
stale_on_error: true,
host_mapping: { 'api.internal': '10.0.0.10' },
},
msgpack: true,
})
// GET
const resp = await client.get('/users/1')
console.log(resp.status, resp.body.toString())
// POST
await client.post('/messages', Buffer.from(JSON.stringify({ text: 'hello' })), {
contentType: 'application/json',
})
// Circuit breaker state
console.log(client.circuitBreakerState()) // 'closed' | 'open' | 'half-open'
// SSE (one-shot stream)
import { SseStream } from '@eric8810/catcher-napi-http/sse'
const stream = new SseStream(
{ url: 'https://stream.example.com/events' },
(event: SseEvent) => {
if (event.type === 'Line') console.log(event.data)
},
)
// later: stream.close()
// SSE (auto-reconnect client)
import { SseClient } from '@eric8810/catcher-napi-http/sse'
const sse = new SseClient(
{
url: 'https://stream.example.com/events',
reconnect: { max_retries: 10, initial_delay_ms: 1000 },
},
(event: SseEvent) => {
if (event.type === 'Line') console.log(event.data)
},
)API
new HttpClient(config: HttpClientConfig | string)
Create a client from a typed config object or JSON string. All fields are optional with sensible defaults. Supports both snake_case and camelCase field names.
interface HttpClientConfig {
base_url?: string
connect_timeout_ms?: number // default: 10000
response_timeout_ms?: number // default: 30000
pool?: PoolConfig
tls?: TlsConfig
dns?: DnsConfig
retry?: RetryConfig
circuit_breaker?: CircuitBreakerConfig
max_concurrency?: number // default: 50
default_headers?: Record<string, string>
hostname_override?: string
proxy?: ProxyConfig
redirect?: RedirectConfig
auth?: { username: string; password: string }
bearer_token?: string
msgpack?: boolean // default: false
}
interface DnsConfig {
cache_size?: number // default: 512
cache_ttl_secs?: number // default: 300
negative_ttl_secs?: number // default: 60
stale_ttl_secs?: number // default: 3600
stale_on_error?: boolean // default: true
nameservers?: string[]
host_mapping?: Record<string, string>
}When msgpack is enabled, JSON request bodies are encoded as MessagePack and MessagePack responses are decoded back to JSON bytes when the response content type contains msgpack.
Methods
| Method | Signature |
|--------|-----------|
| get(url, options?) | async (url: string, options?: RequestOptions) => HttpResponse |
| post(url, body?, options?) | async (url: string, body?: Buffer, options?: RequestOptions) => HttpResponse |
| put(url, body?, options?) | async (url: string, body?: Buffer, options?: RequestOptions) => HttpResponse |
| delete(url, options?) | async (url: string, options?: RequestOptions) => HttpResponse |
| patch(url, body?, options?) | async (url: string, body?: Buffer, options?: RequestOptions) => HttpResponse |
| circuitBreakerState() | () => 'closed' \| 'open' \| 'half-open' |
| metrics() | () => Metrics |
| executeStream(method, url, body?, options?, onChunk?) | (method: string, url: string, body?: Buffer, options?: RequestOptions, onChunk?: (event: StreamEvent) => void) => void |
| setAdaptiveTimeout(min, max, mult, win) | (min: number, max: number, mult: number, win: number) => void |
| cancelAll() | () => void |
| cancelRequest(requestId) | (requestId: number) => boolean |
| nextRequestId() | () => number |
RequestOptions
Per-request options (NAPI-RS auto-generated, camelCase fields):
interface RequestOptions {
headers?: Record<string, string>
timeoutMs?: number
contentType?: string
}HttpResponse
NAPI-RS auto-generated (camelCase fields):
interface HttpResponse {
status: number
headers: Record<string, string>
body: Buffer
elapsedMs: number
}Metrics
Runtime metrics snapshot (NAPI-RS auto-generated, camelCase fields):
interface Metrics {
httpRequests: number
httpSuccessRate: number
httpAvgLatencyUs: number
httpRetries: number
wsConnectSuccessRate: number
wsDisconnects: number
wsMessagesSent: number
wsMessagesReceived: number
cbOpenCount: number
queueTimeouts: number
}Note:
HttpResponse,RequestOptions, andMetricsare auto-generated by NAPI-RS (camelCase fields). JSON config types likeHttpClientConfiguse snake_case (serde-based).
SSE
| Class | Description |
|-------|-------------|
| SseStream | One-shot SSE stream (no auto-reconnect) |
| SseClient | Long-lived SSE client with auto-reconnect |
new SseStream(config: SseClientConfig | string, onEvent: (event: SseEvent) => void)
new SseClient(config: SseClientConfig | string, onEvent: (event: SseEvent) => void)
type SseEvent =
| { type: 'Line'; data: string }
| { type: 'Error'; message: string }
| { type: 'End' }StreamEvent
type StreamEvent =
| { type: 'Headers'; status: number; headers: Record<string, string> }
| { type: 'Chunk'; data: string } // base64 encoded
| { type: 'Done' }
| { type: 'Error'; message: string }Build from Source
Requires Rust toolchain.
npm run build # napi build + tsup compile
npm run build:ts # tsup only (no Rust rebuild)License
MIT
