Packages
http_fetch
0.5.0
0.21.19
0.21.18
0.21.17
0.21.16
0.21.15
0.21.14
0.21.13
0.21.12
0.21.11
0.21.10
0.21.9
0.21.8
0.21.7
0.21.6
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.0
0.19.1
0.19.0
0.18.1
0.18.0
0.17.2
0.17.1
0.17.0
0.16.7
0.16.5
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.0
0.6.0
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.0
A browser-like HTTP fetch API for Elixir using Erlang's built-in socket modules
Current section
Files
Jump to
Current section
Files
http_fetch
README.md
README.md
# HTTP Fetch
[](https://github.com/gsmlg-dev/http_fetch/actions/workflows/elixir.yml)
[](https://hex.pm/packages/phoenix_react_server)
[](https://hexdocs.pm/http_fetch/)
[](https://hex.pm/packages/http_fetch)
[](https://hex.pm/packages/http_fetch)
A modern HTTP client library for Elixir that provides a fetch API similar to web browsers, built on Erlang's built-in `:httpc` module.
## Features
- **Browser-like API**: Familiar fetch interface with promises and async/await patterns
- **Full HTTP support**: GET, POST, PUT, DELETE, PATCH, HEAD methods
- **Complete httpc integration**: Support for all :httpc.request options
- **Form data support**: HTTP.FormData for multipart/form-data and file uploads
- **Streaming file uploads**: Efficient large file uploads using streams
- **Type-safe configuration**: HTTP.FetchOptions for structured request configuration
- **Promise-based**: Async operations with chaining support
- **Request cancellation**: AbortController support for cancelling requests
- **Automatic JSON parsing**: Built-in JSON response handling
- **Zero dependencies**: Uses only Erlang/OTP built-in modules
## Quick Start
```elixir
# Simple GET request
{:ok, response} =
HTTP.fetch("https://jsonplaceholder.typicode.com/posts/1")
|> HTTP.Promise.await()
# Get response data
IO.puts("Status: #{response.status}")
text = HTTP.Response.text(response)
{:ok, json} = HTTP.Response.json(response)
# Read response body as raw binary
response =
HTTP.fetch("https://jsonplaceholder.typicode.com/posts/1")
|> HTTP.Promise.await()
# response.body contains the raw binary data
binary_data = response.body
# POST request with JSON
{:ok, response} =
HTTP.fetch("https://jsonplaceholder.typicode.com/posts", [
method: "POST",
headers: %{"Content-Type" => "application/json"},
body: JSON.encode\!(%{title: "Hello", body: "World"})
])
|> HTTP.Promise.await()
```
# Form data with file upload
```elixir
file_stream = File.stream!("document.pdf")
form = HTTP.FormData.new()
|> HTTP.FormData.append_field("name", "John Doe")
|> HTTP.FormData.append_file("document", "document.pdf", file_stream)
{:ok, response} =
HTTP.fetch("https://api.example.com/upload", [
method: "POST",
body: form
])
|> HTTP.Promise.await()
```
## API Reference
### HTTP.fetch/2
Performs an HTTP request and returns a Promise.
```elixir
promise = HTTP.fetch(url, [
method: "GET",
headers: %{"Accept" => "application/json"},
body: "request body",
content_type: "application/json",
options: [timeout: 10_000],
signal: abort_controller
])
```
Supports both string URLs and URI structs:
```elixir
# String URL
promise = HTTP.fetch("https://api.example.com/data")
# URI struct
uri = URI.parse("https://api.example.com/data")
promise = HTTP.fetch(uri)
```
### HTTP.Promise
Asynchronous promise wrapper for HTTP requests.
```elixir
{:ok, response} = HTTP.Promise.await(promise)
# Promise chaining
HTTP.fetch("https://api.example.com/data")
|> HTTP.Promise.then(fn response -> HTTP.Response.json(response) end)
|> HTTP.Promise.await()
```
### HTTP.Response
Represents an HTTP response.
```elixir
text = HTTP.Response.text(response)
{:ok, json} = HTTP.Response.json(response)
# Access raw response body as binary
response =
HTTP.fetch("https://api.example.com/large-file")
|> HTTP.Promise.await()
# response.body contains the raw binary response data
binary_data = response.body
# Write response to file (supports both streaming and non-streaming)
:ok = HTTP.Response.write_to(response, "/tmp/downloaded-file.txt")
# Write large file downloads directly to disk
{:ok, response} =
HTTP.fetch("https://example.com/large-file.zip")
|> HTTP.Promise.await()
:ok = HTTP.Response.write_to(response, "/tmp/large-file.zip")
```
### HTTP.Headers
Handle HTTP headers with utilities for parsing, normalizing, and manipulating headers.
```elixir
# Create headers
headers = HTTP.Headers.new([{"Content-Type", "application/json"}])
# Get header value
type = HTTP.Headers.get(headers, "content-type")
# Set header
headers = HTTP.Headers.set(headers, "Authorization", "Bearer token")
# Set header only if not already present
headers = HTTP.Headers.set_default(headers, "User-Agent", "CustomAgent/1.0")
# Access default user agent string
default_ua = HTTP.Headers.user_agent()
# Parse Content-Type
{media_type, params} = HTTP.Headers.parse_content_type("application/json; charset=utf-8")
```
### HTTP.Telemetry
Comprehensive telemetry and metrics for HTTP requests and responses.
```elixir
# All HTTP.fetch operations automatically emit telemetry events
# No configuration required - just attach handlers
:telemetry.attach_many(
"my_handler",
[
[:http_fetch, :request, :start],
[:http_fetch, :request, :stop],
[:http_fetch, :request, :exception]
],
fn event_name, measurements, metadata, _config ->
case event_name do
[:http_fetch, :request, :start] ->
IO.puts("Starting request to #{metadata.url}")
[:http_fetch, :request, :stop] ->
IO.puts("Request completed: #{measurements.status} in #{measurements.duration}μs")
[:http_fetch, :request, :exception] ->
IO.puts("Request failed: #{inspect(metadata.error)}")
end
end,
nil
)
# Manual telemetry events (for custom implementations)
HTTP.Telemetry.request_start("GET", URI.parse("https://example.com"), %HTTP.Headers{})
HTTP.Telemetry.request_stop(200, URI.parse("https://example.com"), 1024, 1500)
HTTP.Telemetry.request_exception(URI.parse("https://example.com"), :timeout, 5000)
```
### HTTP.Request
Request configuration struct.
```elixir
request = %HTTP.Request{
method: :post,
url: URI.parse("https://api.example.com/data"),
headers: [{"Authorization", "Bearer token"}],
body: "data",
http_options: [timeout: 10_000, connect_timeout: 5_000],
options: [sync: false, body_format: :binary]
}
```
**Field Mapping to :httpc.request/4:**
- `http_options`: 3rd argument (request-specific HTTP options)
- `options`: 4th argument (client-specific options)
### HTTP.FormData
Handle form data and file uploads.
```elixir
# Regular form data
form = HTTP.FormData.new()
|> HTTP.FormData.append_field("name", "John")
|> HTTP.FormData.append_field("email", "john@example.com")
# File upload
file_stream = File.stream!("document.pdf")
form = HTTP.FormData.new()
|> HTTP.FormData.append_field("name", "John")
|> HTTP.FormData.append_file("document", "document.pdf", file_stream, "application/pdf")
# Use in request
HTTP.fetch("https://api.example.com/upload", method: "POST", body: form)
```
### HTTP.AbortController
Request cancellation.
```elixir
controller = HTTP.AbortController.new()
HTTP.AbortController.abort(controller)
```
## Error Handling
The library handles:
- Network errors and timeouts
- HTTP error status codes
- JSON parsing errors
- Invalid URLs
- Cancelled requests
## License
MIT License