HTTPower.Adapter behaviour (HTTPower v0.23.0)

Copy Markdown View Source

Behaviour for HTTPower adapters.

Adapters are responsible for making the actual HTTP requests using a specific HTTP client library (Req, Tesla, etc.). HTTPower's features like retry logic, circuit breakers, and rate limiting sit above the adapter layer.

Implementing an Adapter

To implement an adapter, create a module that implements the request/5 callback:

defmodule MyApp.CustomAdapter do
  @behaviour HTTPower.Adapter

  @impl true
  def request(method, url, body, headers, opts) do
    # Make HTTP request using your preferred HTTP client
    # Convert response to {:ok, %HTTPower.Response{}} or {:error, reason}
  end
end

Built-in Adapters

HTTPower ships with three built-in adapters:

Using an Adapter

Specify the adapter when making requests:

# Use default (Finch) adapter
HTTPower.get("https://api.example.com")

# Use explicit Req adapter
HTTPower.get("https://api.example.com", adapter: HTTPower.Adapter.Req)

# Use Tesla adapter with your Tesla client
tesla_client = Tesla.client([Tesla.Middleware.JSON])
HTTPower.get("https://api.example.com",
  adapter: {HTTPower.Adapter.Tesla, tesla_client})

Adapter Contract

Adapters must return responses in a standardized format:

  • Success: {:ok, %HTTPower.Response{status: integer(), headers: map(), body: term()}}
  • Failure: {:error, reason} where reason can be an atom, string, or exception

HTTPower's retry logic and error handling work consistently across all adapters.

Summary

Callbacks

Makes an HTTP request using the adapter's underlying HTTP client.

Functions

Normalizes response headers into a %{String.t() => [String.t()]} map.

Prepares request headers, ensuring a valid map is returned.

Callbacks

request(method, url, body, headers, opts)

@callback request(
  method :: atom(),
  url :: URI.t(),
  body :: term(),
  headers :: map(),
  opts :: keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, term()}

Makes an HTTP request using the adapter's underlying HTTP client.

Parameters

  • method - HTTP method as an atom (:get, :post, :put, :delete)
  • url - Full URL as a URI struct
  • body - Request body (string, map, or nil)
  • headers - Map of request headers
  • opts - Keyword list of adapter-specific options

Returns

  • {:ok, %HTTPower.Response{}} on successful request
  • {:error, reason} on failure

Examples

@impl true
def request(:get, "https://api.example.com/users", nil, %{}, _opts) do
  {:ok, %HTTPower.Response{
    status: 200,
    headers: %{"content-type" => "application/json"},
    body: %{"users" => []}
  }}
end

Functions

normalize_response_headers(headers)

@spec normalize_response_headers([{term(), term()}] | map()) :: %{
  required(String.t()) => [String.t()]
}

Normalizes response headers into a %{String.t() => [String.t()]} map.

HTTP headers can repeat (e.g. Set-Cookie), so values are always lists. Accepts either a list of {key, value} tuples (as Finch, Tesla, and Req < 0.5 return) or a map whose values are strings or lists (as Req >= 0.5 returns), producing a single consistent shape across all adapters. Keys are downcased to strings; tuple order is preserved for repeated keys.

prepare_headers(headers)

@spec prepare_headers(map() | nil) :: map()

Prepares request headers, ensuring a valid map is returned.

Used by all adapters and the test interceptor to ensure consistent header handling across the library.