Skip to main content
Openfuse
Guides

Protect calls

Timeouts, fallbacks, cancellation, and patterns

protect() wraps a function call with breaker state checks, automatic metrics, optional timeouts, and fallback handling.

Basic usage

const customer = await openfuse.breaker('stripe').protect(
  () => stripe.customers.retrieve(customerId),
)

Before executing your function, protect() checks the breaker's state. If the breaker is closed or half-open, your function runs. If it's open, the behavior depends on whether you provided a fallback.

Add a timeout

const customer = await openfuse.breaker('stripe').protect(
  (signal) => stripe.customers.retrieve(customerId, { signal }),
  { timeout: 3000 },
)

If your function doesn't resolve within 3,000ms, the SDK throws a TimeoutError. The signal is an AbortSignal for cooperative cancellation with downstream libraries.

The timeout applies to your function only, not to the SDK's internal state check.

Add a fallback

const customer = await openfuse.breaker('stripe').protect(
  () => stripe.customers.retrieve(customerId),
  { fallback: () => cachedCustomer },
)

When the breaker is open, the fallback runs instead of your function. No error is thrown.

Fallbacks are not wrapped with a timeout or AbortSignal. If your fallback calls external services, apply your own timeout.

Timeout + fallback

const customer = await openfuse.breaker('stripe').protect(
  (signal) => stripe.customers.retrieve(customerId, { signal }),
  {
    timeout: 3000,
    fallback: () => cachedCustomer,
  },
)

The fallback only runs when the breaker is open. If the breaker is closed and your function times out, a TimeoutError is thrown. The fallback is not called on timeout.

Use AbortSignal for cancellation

const controller = new AbortController()

setTimeout(() => controller.abort(), 5000)

const customer = await openfuse.breaker('stripe').protect(
  (signal) => stripe.customers.retrieve(customerId, { signal }),
  { signal: controller.signal },
)

When you pass a signal option, it's combined with the timeout signal. If either signal aborts, the function is cancelled and an AbortOperationError is thrown.

Behavior when the breaker is open

Fallback provided?Behavior
YesFallback executes. No error thrown. No metrics recorded.
NoFunction executes anyway (fail-open) with a warning logged. Metrics are recorded.

The SDK never silently drops requests. If there's no fallback, it executes your function even when the breaker is open.

Behavior on timeout

When a timeout is set and the function exceeds it:

  1. The AbortSignal passed to your function is triggered.
  2. A TimeoutError is thrown.
  3. The execution is recorded as a timeout in metrics.

Reuse breaker handles

If you call the same breaker frequently, save the handle to avoid repeated lookups:

const stripeBreaker = openfuse.breaker('stripe')

// Later, in your request handler:
const customer = await stripeBreaker.protect(
  () => stripe.customers.retrieve(customerId),
)

breaker() returns a lightweight BreakerHandle bound to the slug. No API call is made. Breakers are automatically created in Openfuse when first seen.

Real-world example

import { OpenfuseCloud, TimeoutError } from '@openfuseio/sdk'

const openfuse = new OpenfuseCloud({
  system: 'payments',
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
})

openfuse.init()

async function getCustomer(customerId: string) {
  try {
    return await openfuse.breaker('stripe-get-customer').protect(
      (signal) => stripe.customers.retrieve(customerId, { signal }),
      {
        timeout: 3000,
        fallback: () => cachedCustomer,
      },
    )
  } catch (error) {
    if (error instanceof TimeoutError) {
      return cachedCustomer
    }
    throw error
  }
}

On this page