HTTPower (HTTPower v0.16.0)

View Source

A production-ready HTTP client library for Elixir that adds reliability patterns and enterprise features on top of existing HTTP clients through an adapter system.

HTTPower supports multiple HTTP clients via adapters — Finch (high-performance, default), Req (batteries-included), and Tesla (bring-your-own-config) — while providing production reliability features that work consistently across all of them:

  • Adapter pattern: Choose between Finch, Req, or Tesla HTTP clients
  • Middleware pipeline: Rate limiting, circuit breaker, and request deduplication
  • Smart retries: Exponential backoff with jitter and Retry-After header support
  • PCI-compliant logging: Automatic sanitization of sensitive data with structured metadata
  • Telemetry integration: Comprehensive observability for all operations
  • Configuration profiles: Pre-built profiles for payment processing, high-volume APIs, and microservices
  • Clean error handling: Never raises exceptions, always returns {:ok, response} or {:error, reason}
  • Test utilities: Adapter-agnostic test helpers via HTTPower.Test

Basic Usage

# Simple GET request
HTTPower.get("https://api.example.com/users")

# POST with data
HTTPower.post("https://api.example.com/users",
  body: "name=John&email=john@example.com",
  headers: %{"Content-Type" => "application/x-www-form-urlencoded"}
)

# With configuration options
HTTPower.get("https://api.example.com/slow-endpoint",
  timeout: 30,
  max_retries: 5,
  retry_safe: true
)

Test Mode

HTTPower can block real HTTP requests during testing while allowing mocked requests:

# In test configuration
Application.put_env(:httpower, :test_mode, true)

# This will be blocked
HTTPower.get("https://real-api.com")  # {:error, %HTTPower.Error{reason: :network_blocked}}

# But this will work with Req.Test
HTTPower.get("https://api.com", plug: {Req.Test, MyApp})

Configuration Options

  • timeout - Request timeout in seconds (default: 60)
  • max_retries - Maximum retry attempts (default: 3)
  • retry_safe - Enable retries for connection resets (default: false)
  • ssl_verify - Enable SSL verification (default: true)
  • proxy - Proxy configuration (default: :system)
  • headers - Request headers map

Return Values

All HTTP methods return either:

  • {:ok, %HTTPower.Response{}} on success
  • {:error, %HTTPower.Error{}} on failure

HTTPower never raises exceptions for network errors, ensuring your application stays stable even when external services fail.

Configured Clients

You can create pre-configured client instances for reuse:

# Create a configured client
client = HTTPower.new(
  base_url: "https://api.example.com",
  headers: %{"Authorization" => "Bearer token"},
  timeout: 30,
  max_retries: 5
)

# Use the client for multiple requests
HTTPower.get(client, "/users")
HTTPower.post(client, "/users", body: %{name: "John"})

This is especially useful for API clients, different environments, or service-specific configuration.

Summary

Types

client()

@type client() :: %HTTPower{base_url: String.t() | nil, options: keyword()}

Functions

delete(url_or_client, opts_or_path \\ [])

@spec delete(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec delete(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP DELETE request.

Accepts either a URL string or a configured client as the first argument.

Options

See module documentation for available options.

Examples

# With URL string
HTTPower.delete("https://api.example.com/users/1")

# With configured client
client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.delete(client, "/users/1")

delete(client, path, opts)

@spec delete(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

get(url_or_client, opts_or_path \\ [])

@spec get(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec get(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP GET request.

Accepts either a URL string or a configured client as the first argument.

Options

See module documentation for available options.

Examples

# With URL string
HTTPower.get("https://api.example.com/users")
HTTPower.get("https://api.example.com/users", headers: %{"Authorization" => "Bearer token"})

# With configured client
client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.get(client, "/users")

get(client, path, opts)

@spec get(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

head(url_or_client, opts_or_path \\ [])

@spec head(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec head(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP HEAD request.

Accepts either a URL string or a configured client as the first argument.

Examples

HTTPower.head("https://api.example.com/users")

client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.head(client, "/users")

head(client, path, opts)

@spec head(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

new(opts \\ [])

@spec new(keyword()) :: client()

Creates a new HTTPower client with pre-configured options.

Options

  • base_url - Base URL to prepend to all requests
  • profile - Pre-configured profile (:payment_processing, :high_volume_api, :microservices_mesh)
  • All other options are the same as individual request options (see module documentation)

When using a profile, profile settings are merged with explicit options. Explicit options always take precedence over profile defaults.

Examples

# Simple client with base URL
client = HTTPower.new(base_url: "https://api.example.com")

# Client with authentication and timeouts
client = HTTPower.new(
  base_url: "https://api.example.com",
  headers: %{"Authorization" => "Bearer token"},
  timeout: 30,
  max_retries: 5,
  retry_safe: true
)

# Use a profile for optimal settings
client = HTTPower.new(
  base_url: "https://payment-gateway.com",
  profile: :payment_processing
)

# Profile with overrides
client = HTTPower.new(
  base_url: "https://api.example.com",
  profile: :high_volume_api,
  rate_limit: [requests: 2000]  # Override profile's rate limit
)

options(url_or_client, opts_or_path \\ [])

@spec options(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec options(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP OPTIONS request.

Accepts either a URL string or a configured client as the first argument.

Examples

HTTPower.options("https://api.example.com/users")

client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.options(client, "/users")

options(client, path, opts)

@spec options(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

patch(url_or_client, opts_or_path \\ [])

@spec patch(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec patch(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP PATCH request.

Accepts either a URL string or a configured client as the first argument.

Examples

HTTPower.patch("https://api.example.com/users/1", body: "name=Jane")

client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.patch(client, "/users/1", body: %{name: "Jane"})

patch(client, path, opts)

@spec patch(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

post(url_or_client, opts_or_path \\ [])

@spec post(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec post(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP POST request.

Accepts either a URL string or a configured client as the first argument.

Options

See module documentation for available options. Additionally supports:

  • body - Request body (string or form data)

Examples

# With URL string
HTTPower.post("https://api.example.com/users", body: "name=John")
HTTPower.post("https://api.example.com/users",
  body: Jason.encode!(%{name: "John"}),
  headers: %{"Content-Type" => "application/json"}
)

# With configured client
client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.post(client, "/users", body: %{name: "John"})

post(client, path, opts)

@spec post(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

put(url_or_client, opts_or_path \\ [])

@spec put(
  String.t(),
  keyword()
) :: {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}
@spec put(client(), String.t()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

Makes an HTTP PUT request.

Accepts either a URL string or a configured client as the first argument.

Options

See module documentation for available options. Additionally supports:

  • body - Request body (string or form data)

Examples

# With URL string
HTTPower.put("https://api.example.com/users/1", body: "name=John")

# With configured client
client = HTTPower.new(base_url: "https://api.example.com")
HTTPower.put(client, "/users/1", body: %{name: "John"})

put(client, path, opts)

@spec put(client(), String.t(), keyword()) ::
  {:ok, HTTPower.Response.t()} | {:error, HTTPower.Error.t()}

test_mode?()

@spec test_mode?() :: boolean()

Checks if HTTPower is currently in test mode.

In test mode, real HTTP requests are blocked unless they include a :plug option for mocking with Req.Test.

Examples

Application.put_env(:httpower, :test_mode, true)
HTTPower.test_mode?() # true

Application.put_env(:httpower, :test_mode, false)
HTTPower.test_mode?() # false