MoneyHub.Client (MoneyHub v1.0.0)

Copy Markdown View Source

Low-level HTTP client for the Moneyhub data API (api_url).

Every resource module (MoneyHub.Accounts, MoneyHub.Transactions, MoneyHub.Payments, ...) delegates its HTTP calls through here, so retry behaviour, rate-limit handling, and error normalisation are implemented exactly once.

You generally won't call this module directly - use the resource modules instead. It's part of the public API for advanced use cases (calling endpoints not yet wrapped by a dedicated module) via request/2.

Retries

Moneyhub's rate limit (per their docs) is approximately 1000 requests per minute per client, returning 429 with a Retry-After header when exceeded. request/2 retries 429 responses automatically (honouring Retry-After, capped by :max_retry_after_ms) and retries 5xx/network errors with capped exponential backoff, up to :max_retries (default 2).

Emits [:money_hub, :request, :start | :stop | :exception] telemetry events around every call.

Summary

Functions

Issues an HTTP request against the data API.

Safely unwraps a %{"data" => [...]} envelope, as returned by most list endpoints, falling back to the raw body unchanged when it isn't a map with a "data" key (for example if the body is already a bare list).

Types

method()

@type method() :: :get | :post | :patch | :put | :delete

token()

@type token() :: String.t()

Functions

request(config, opts)

@spec request(
  MoneyHub.Config.t(),
  keyword()
) :: {:ok, Req.Response.t()} | {:error, MoneyHub.Error.t()}

Issues an HTTP request against the data API.

Options

  • :method - required, one of :get, :post, :patch, :put, :delete.
  • :path - required, relative to config.api_url, e.g. "/accounts".
  • :token - required, a bearer access token.
  • :query - a map/keyword list of query parameters.
  • :json - a request body to be JSON-encoded.
  • :max_retries - overrides the default retry count.
  • :max_retry_after_ms - caps how long a Retry-After driven sleep may be, to bound worst-case latency.

unwrap_list(body)

@spec unwrap_list(term()) :: term()

Safely unwraps a %{"data" => [...]} envelope, as returned by most list endpoints, falling back to the raw body unchanged when it isn't a map with a "data" key (for example if the body is already a bare list).

Resource modules use this instead of response.body["data"] || response.body directly, since that pattern raises ArgumentError when response.body is a list (lists only support atom-keyed Access calls).