Client Plugins
Retry, circuit breaker, timeout, batching, deduplication, CSRF, and OpenTelemetry tracing for the Silgi client.
Client plugins wrap the transport link to add resilience, performance, and security features. They are composable — stack them in any order.
import { } from 'silgi/client'
import { } from 'silgi/client/fetch'
import { , , } from 'silgi/client/plugins'
const = ({ : 'https://api.example.com' })
const = (((, { : 5000 }), { : 5 }), {
: 3,
})
const = <>()Retry
Automatically retry failed requests with exponential backoff and jitter.
import { } from 'silgi/client/plugins'
const = (baseLink, {
: 3, // default: 3
: 1000, // 1s → 2s → 4s (exponential)
: true, // adds 0-25% random jitter (default: true)
: [500, 502, 503, 504], // status codes to retry on
: (, ) => < 5,
: ({ , , , }) => {
.(`Retry #${} for ${.('/')} in ${}ms`)
},
})| Option | Type | Default | Description |
|---|---|---|---|
maxRetries | number | 3 | Maximum retry attempts |
baseDelay | number | (attempt) => number | 1000 | Base delay in ms. Actual: baseDelay * 2^attempt + jitter |
jitter | boolean | true | Add 0-25% random delay to prevent thundering herd |
retryOn | number[] | [408, 429, 500, 502, 503, 504] | HTTP status codes that trigger retry |
shouldRetry | (error, attempt) => boolean | — | Custom predicate to stop retrying |
onRetry | (info) => void | — | Called before each retry with attempt, delay, error, path |
respectRetryAfter | boolean | true | Parse Retry-After header from 429/503 responses for delay |
Network errors (no status code) are always retried unless shouldRetry returns false.
When respectRetryAfter is enabled (the default), the plugin reads the Retry-After header from 429 and 503 responses. It supports both delay-seconds (Retry-After: 2) and HTTP-date formats. When present, the header value overrides the exponential backoff delay.
The retry plugin respects AbortSignal — if the signal is aborted during a delay wait, the retry is cancelled
immediately.
Circuit Breaker
Prevents cascading failures by blocking requests when a service is down.
import { } from 'silgi/client/plugins'
const = (baseLink, {
: 5, // open after 5 consecutive failures
: 30000, // try again after 30s
: (, { }) => {
.(`Circuit: ${} (${} failures)`)
},
})
// Check state programmatically
.() // 'closed' | 'open' | 'half-open'
.() // manually close the circuit| Option | Type | Default | Description |
|---|---|---|---|
failureThreshold | number | 5 | Consecutive failures before opening |
resetTimeout | number | 30000 | Ms to wait before half-open test |
onStateChange | (state, info) => void | — | Called on state transitions |
State machine
CLOSED → (failures >= threshold) → OPEN
OPEN → (resetTimeout elapsed) → HALF-OPEN
HALF-OPEN → (success) → CLOSED
HALF-OPEN → (failure) → OPENWhen open, requests throw CircuitBreakerOpenError immediately — no network call is made.
Timeout
Set a per-link timeout for all requests.
import { } from 'silgi/client/plugins'
const = (baseLink, { : 5000 })| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 30000 | Timeout in ms |
The timeout signal is combined with any existing signal using AbortSignal.any() — whichever fires first wins.
Composition
Plugins are pure link wrappers — compose them in any order. The outermost plugin runs first:
// Request flow: timeout check → circuit breaker check → retry loop → actual fetch
const = withRetry(withCircuitBreaker(withTimeout(baseLink, { : 5000 })))Recommended order for production:
- withRetry (outermost) — retries the entire inner chain
- withCircuitBreaker — blocks when service is down
- withTimeout (innermost) — aborts slow individual requests
OpenTelemetry
Wrap any link with automatic span creation for distributed tracing:
import { } from 'silgi/client/plugins'
import { } from '@opentelemetry/api'
const = (baseLink, {
: .getTracer('my-service'),
})Each call creates a span named rpc.client/{procedure} with attributes:
| Attribute | Value |
|---|---|
rpc.system | silgi |
rpc.method | Procedure path (e.g. users.list) |
On error, the span records an exception event and sets status code 2. The same Tracer interface works for both server-side (otelWrap) and client-side (withOtel).
Other plugins
Batch
Combine multiple RPC calls into a single HTTP request:
import { } from 'silgi/client/plugins'
const = new ({ : '/batch', : 10 })Dedupe
Deduplicate identical in-flight requests:
import { } from 'silgi/client/plugins'
const = (baseLink)CSRF
Add CSRF token to requests:
import { } from 'silgi/client/plugins'
const = (baseLink, { : 'x-csrf-token' })