cruel

resilience

circuit breaker, retry, bulkhead, timeout, fallback

circuit breaker

const api = cruel.circuitBreaker(fetch, {
  threshold: 5,
  timeout: 30000,
  onOpen: () => console.log("circuit opened"),
  onClose: () => console.log("circuit closed"),
})

await api("...")
api.getState() // { state: "closed", failures: 0 }

retry with backoff

const api = cruel.retry(fetch, {
  attempts: 3,
  delay: 1000,
  backoff: "exponential",
  maxDelay: 10000,
  onRetry: (attempt, error) => console.log(`retry ${attempt}`),
  retryIf: (error) => (error as { statusCode?: number }).statusCode !== 404,
})

bulkhead

const api = cruel.bulkhead(fetch, {
  maxConcurrent: 10,
  maxQueue: 100,
  onReject: () => console.log("rejected"),
})

timeout

const api = cruel.withTimeout(fetch, {
  ms: 5000,
  onTimeout: () => console.log("timed out"),
})

fallback

const api = cruel.fallback(fetch, {
  fallback: cachedData,
  onFallback: (error) => console.log("using fallback"),
})

combine patterns

import { cruel } from "cruel"

const resilientApi = cruel.retry(
  cruel.circuitBreaker(
    cruel(fetch, { fail: 0.1, delay: [100, 500] }),
    { threshold: 5, timeout: 30000 }
  ),
  { attempts: 3, backoff: "exponential" }
)