Home/Docs/http-client
HC

http-client

v0.2.0

@backendkit-labs/http-client

Every HTTP call returns Result<T, E> — no try/catch anywhere.

Overview#

Making HTTP calls is one of the most common and most failure-prone backend tasks. Popular libraries like Axios and Got treat failures as exceptions — forcing every caller to wrap calls with try/catch, manage retries manually, and wire up circuit breakers separately.

@backendkit-labs/http-client is built on Axios for ecosystem compatibility but integrates the full BackendKit pattern stack natively:

  • Every response is Result<T, HttpClientError> — no try/catch anywhere.
  • Built-in exponential backoff + jitter retry for transient errors.
  • Integrated circuit breaker using the same isFailure semantics as @backendkit-labs/circuit-breaker.
  • Cancellation by key (for polling) and mass cancellation when shutting down.
  • Middleware as pipeline steps: auth headers, logging, rate limiting — all composable and testable.
  • defineHttpClient<T>() creates fully typed client contracts injectable via NestJS DI.

Configuration#

npm install @backendkit-labs/http-client axios
PropTypeDescription
baseUrlstringBase URL prepended to all requests.
timeoutnumberRequest timeout in milliseconds.
defaultHeadersRecord<string, string>Headers included on every request.
retry.attemptsnumberNumber of retry attempts after the initial failure.
retry.baseDelaynumberInitial delay in ms. Doubles on each retry (exponential backoff + jitter).
retry.retryOnstring[]Error types to retry: 'network_error' | 'timeout'.
circuitBreakerCircuitBreakerConfigInline circuit breaker — same isFailure semantics as @backendkit-labs/circuit-breaker.

Making Requests#

PropTypeDescription
get<T>(path, cfg?)Promise<Result<T, E>>GET request.
post<T>(path, body, cfg?)Promise<Result<T, E>>POST with JSON body.
put<T>(path, body, cfg?)Promise<Result<T, E>>PUT with JSON body.
patch<T>(path, body, cfg?)Promise<Result<T, E>>PATCH with JSON body.
delete<T>(path, cfg?)Promise<Result<T, E>>DELETE request.
cancelByKey(key)voidCancel all in-flight requests with the given key. Useful for polling.
cancelAll()voidCancel every in-flight request — use on shutdown.

Error Types#

PropTypeDescription
timeoutHttpClientErrorRequest exceeded the configured timeout.
network_errorHttpClientErrorConnection refused, DNS failure, or network unreachable.
http_errorHttpClientErrorServer responded with 4xx or 5xx. Includes status and response body.
circuit_openHttpClientErrorIntegrated circuit breaker is open — request was not sent.
cancelledHttpClientErrorRequest was cancelled via cancelByKey() or cancelAll().

Retry & Circuit Breaker#

Retry uses exponential backoff with jitter to prevent thundering herd: each attempt waits baseDelay × 2ⁿ + random ms. The circuit breaker wraps the full retry cycle — if the breaker opens mid-retry, the attempt fails immediately without exhausting remaining retries.

Typed NestJS clients

defineHttpClient<T>() creates a strongly-typed DI token. Register instances once in the module; inject by token anywhere.

payment.module.ts
import { HttpClientModule, defineHttpClient } from '@backendkit-labs/http-client/nestjs';

export const STRIPE_CLIENT = defineHttpClient<StripeClient>();

@Module({
  imports: [
    HttpClientModule.forRoot({
      clients: [
        {
          token: STRIPE_CLIENT,
          config: {
            baseUrl: 'https://api.stripe.com/v1',
            timeout: 10_000,
            retry: { attempts: 3, baseDelay: 500 },
            circuitBreaker: { name: 'stripe', failureThreshold: 40 },
          },
        },
      ],
    }),
  ],
})
export class PaymentModule {}

@Injectable()
export class PaymentService {
  constructor(
    @InjectHttpClient(STRIPE_CLIENT) private readonly stripe: StripeClient,
  ) {}
}

Examples#

From basic to production-grade — copy and adapt.

github.client.ts
import { HttpClient } from '@backendkit-labs/http-client';

const github = new HttpClient({
  baseUrl: 'https://api.github.com',
  timeout: 8_000,
  defaultHeaders: {
    Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
    Accept: 'application/vnd.github.v3+json',
  },
});

const result = await github.get<Repository>('/repos/owner/repo');

if (result.ok) {
  return result.value.stargazers_count; // typed Repository
}
// result.error.type: 'timeout' | 'network_error' | 'http_error'

⚖️ vs. Alternatives#

Comparing against axios and got — the two most popular HTTP clients in the Node.js ecosystem.

Feature@backendkit-labs/http-clientaxiosgot
Result<T,E> responses✅ Always❌ Throws AxiosError❌ Throws
Built-in retry✅ Exponential backoff❌ Manual / plugin✅ Built-in
Circuit breaker✅ Integrated
Typed error variants✅ timeout/network/http_error⚠️ Generic AxiosError⚠️ Partial
No try/catch required
NestJS integration✅ @InjectHttpClient⚠️ @nestjs/axios
Named instances (DI)⚠️ Manual
Runtime dependenciesaxios (peer)0 (is the dep)0
Weekly downloadsGrowing~50M~2M

✅ Supported  ·  ❌ Not supported  ·  ⚠️ Partial / workaround needed. Download counts are approximate weekly npm averages.