Algoliax.HttpClient behaviour (Algoliax v0.11.0)

View Source

Behaviour for the HTTP client used by Algoliax to talk to the Algolia API.

Algoliax ships no HTTP client of its own. Each application using Algoliax provides its own implementation and configures it:

config :algoliax, :http_client, MyApp.AlgoliaHttpClient

This keeps you free to use whichever HTTP library you already depend on (Req, Finch, hackney, :httpc, ...).

Verify TLS certificates

Algoliax sends your Algolia API key as a header on every request. Make sure your implementation verifies TLS certificates. Req/Finch/Mint do so by default, but Erlang's :httpc/:ssl default to verify_none — if you build an adapter on those, pass ssl: [verify: :verify_peer, ...] explicitly.

Implementing the behaviour

defmodule MyApp.AlgoliaHttpClient do
  @behaviour Algoliax.HttpClient

  @impl Algoliax.HttpClient
  def request(opts) do
    # perform the HTTP call described by `opts`
    {:ok, status, headers, body}
  end
end

Supported options

The opts keyword list passed to request/1 contains at minimum:

  • :method - HTTP verb as an atom (:get, :post, :put, :delete)
  • :url - full URL string (query parameters already appended)
  • :headers - list of {name, value} tuples
  • :body - request body as a binary (already JSON-encoded by Algoliax), or nil
  • :receive_timeout - response timeout in milliseconds

Unknown options should be silently ignored.

Retries and redirects

Implementations must disable any retry and redirect-following their underlying HTTP library performs by default:

  • Algoliax already retries transport failures itself, rotating to a different Algolia host on each attempt. A client that also retries would hammer the same already-failed host instead of letting Algoliax rotate.
  • Algoliax routes 3xx/4xx status codes to its own error handling, so a client that transparently follows redirects would hide those responses.

The shipped reference adapter does this with Req's retry: false and redirect: false options; use the equivalent for your library.

Return value

On a completed HTTP exchange (any status code), return:

{:ok, status :: non_neg_integer(), headers :: [{String.t(), String.t()}], body :: binary()}

The body is always a raw binary — Algoliax decodes the JSON itself.

On a transport-level failure (connection refused, timeout, DNS error), return:

{:error, reason :: term()}

Algoliax retries transport failures against Algolia's retry hosts.

Contract compatibility

The request/1 contract intentionally mirrors a generic {:ok, status, headers, body} / {:error, reason} shape, so an existing shared HTTP-client behaviour with the same signature is a good starting point. A matching signature is necessary but not sufficient, though — before configuring such a module directly, confirm it also:

  • returns the exact {:ok, status, headers, body} 4-tuple (a raw binary body, not a decoded map or a wrapper struct), and
  • disables its own retry and redirect-following (see Retries and redirects above).

A return value outside the contract raises Algoliax.HttpClientContractError rather than being silently retried, so a mismatch surfaces immediately.

Summary

Functions

Returns the configured HTTP client implementation.

Types

method()

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

opts()

@type opts() :: [
  method: method(),
  url: String.t(),
  headers: [{String.t(), String.t()}],
  body: binary() | nil,
  receive_timeout: non_neg_integer()
]

result()

@type result() ::
  {:ok, status :: non_neg_integer(), headers :: [{String.t(), String.t()}],
   body :: binary()}
  | {:error, reason :: term()}

Callbacks

request(opts)

@callback request(opts()) :: result()

Functions

impl()

@spec impl() :: module()

Returns the configured HTTP client implementation.

Raises a helpful error if none is configured, or if the configured value is clearly not a module name (nil, a boolean, or a non-atom). The module is not loaded or otherwise validated here — a typo'd or non-implementing module name still passes and fails when first called.