Skip to content

Concepts

One Client per API

An HTTP Guardian client represents one target API: baseUrl, auth, parser, cache, guards, notifier, and plugins. If the application talks to two backends, create two clients. This avoids conditional options everywhere and makes redirects, allowed origins, and global headers easier to audit.

Explicit Result

The get, post, put, patch, and delete calls return a ClientResult<T>. Consumers check result.ok instead of catching exceptions for normal API errors.

type ClientResult<T> =
  | { ok: true; status: number; headers: Headers; data: T; raw: unknown; url: string }
  | { ok: false; status: number; headers: Headers; errors: AppError[]; raw: unknown; url: string };

A network error, an HTTP error, and a parsing error are therefore exposed through the same application-side handling model.

Parse before UI

The parser transforms the raw response into an application contract before the UI or business services touch it. A REST, Laravel, Nest, GraphQL, or custom envelope must be normalized once.

The rule is simple: if two components know the backend's raw response shape, the parser is probably in the wrong place.

Guards as a Pipeline

Guards behave like middleware around fetch. They receive a context, can enrich headers or metadata, call next(ctx), then inspect the response.

Recommended order:

  1. Internal auth.
  2. CSRF or correlation.
  3. Idempotency and retry.
  4. Auth refresh.
  5. Instrumentation and notifications.

The order must be documented in the consuming application when it has product consequences.

Plugins for Cross-Cutting Policies

Plugins avoid repeating logger, Sentry, OpenTelemetry, retry, timeout, or rate limit logic in every service. They are useful when the behavior belongs to the whole API, not to a single request.