README.md

<div align="center">
  <img src="https://raw.githubusercontent.com/TrustBound/dream/main/ricky_and_lucy.png" alt="Dream Logo" width="200">
  
  <a href="https://hexdocs.pm/dream_http_client">
    <img src="https://img.shields.io/badge/hex-docs-lightgreen.svg" alt="HexDocs">
  </a>
</div>

# dream_http_client

**Type-safe HTTP client for Gleam with streaming support.**

A standalone HTTP/HTTPS client built on Erlang's battle-tested `httpc`. Supports both streaming and non-streaming requests. Built with the same quality standards as [Dream](https://github.com/TrustBound/dream), but completely independent—use it in any Gleam project.

## Features

- ✅ Streaming and non-streaming modes
- ✅ HTTPS support (TLS/SSL)
- ✅ Builder pattern for request configuration
- ✅ Type-safe with `Result` types
- ✅ Battle-tested Erlang `httpc` under the hood
- ✅ Zero dependencies on Dream or other frameworks

## Installation

```bash
gleam add dream_http_client
```

## Quick Start

### Non-Streaming Requests

Perfect for JSON APIs and small responses:

```gleam
import dream_http_client/client
import dream_http_client/fetch
import gleam/http

let result = client.new
  |> client.method(http.Get)
  |> client.scheme(http.Https)
  |> client.host("api.example.com")
  |> client.path("/users/123")
  |> client.add_header("Authorization", "Bearer " <> token)
  |> fetch.request()

case result {
  Ok(body) -> decode_json(body)
  Error(msg) -> handle_error(msg)
}
```

### Streaming Requests

Perfect for large files, AI responses, or real-time data:

```gleam
import dream_http_client/client
import dream_http_client/stream
import gleam/http
import gleam/yielder

client.new
  |> client.host("cdn.example.com")
  |> client.path("/large-file.zip")
  |> stream.stream_request()
  |> yielder.each(fn(chunk) {
    // Process each chunk as it arrives
    save_chunk(chunk)
  })
```

## Usage

### Building Requests

Use the builder pattern to configure requests:

```gleam
import dream_http_client/client
import gleam/http

let request = client.new
  |> client.method(http.Post)
  |> client.scheme(http.Https)
  |> client.host("api.example.com")
  |> client.port(443)
  |> client.path("/api/users")
  |> client.query("page=1&limit=10")
  |> client.add_header("Content-Type", "application/json")
  |> client.add_header("Authorization", "Bearer " <> token)
  |> client.body(json_body)
```

### Non-Streaming (Fetch)

Get the complete response body at once:

```gleam
import dream_http_client/fetch

case fetch.request(client_request) {
  Ok(body) -> {
    // Process complete response
    case json.decode(body, user_decoder) {
      Ok(user) -> Ok(user)
      Error(_) -> Error("Invalid JSON")
    }
  }
  Error(msg) -> Error("Request failed: " <> msg)
}
```

### Streaming

Process chunks as they arrive:

```gleam
import dream_http_client/stream
import gleam/yielder
import gleam/bytes_tree

// Process chunks incrementally
stream.stream_request(client_request)
  |> yielder.each(fn(chunk) {
    let text = bytes_tree.to_string(chunk)
    io.println("Received: " <> text)
  })

// Or collect all chunks
let chunks = stream.stream_request(client_request)
  |> yielder.to_list()
```

### POST Requests with JSON

```gleam
import dream_http_client/client
import dream_http_client/fetch
import gleam/http
import gleam/json

let user_json = json.object([
  #("name", json.string("Alice")),
  #("email", json.string("alice@example.com")),
])

let request = client.new
  |> client.method(http.Post)
  |> client.host("api.example.com")
  |> client.path("/users")
  |> client.add_header("Content-Type", "application/json")
  |> client.body(json.to_string(user_json))
  |> fetch.request()
```

## API Reference

### Client Configuration

- `client.new` - Default request configuration
- `client.method(req, method)` - Set HTTP method
- `client.scheme(req, scheme)` - Set protocol (HTTP/HTTPS)
- `client.host(req, host)` - Set hostname
- `client.port(req, port)` - Set port number
- `client.path(req, path)` - Set request path
- `client.query(req, query)` - Set query string
- `client.body(req, body)` - Set request body
- `client.add_header(req, name, value)` - Add header
- `client.headers(req, headers)` - Replace all headers

### Request Execution

- `fetch.request(req) -> Result(String, String)` - Non-streaming request
- `stream.stream_request(req) -> Yielder(BytesTree)` - Streaming request

## Design Principles

This module follows the same quality standards as [Dream](https://github.com/TrustBound/dream):

- **No nested cases** - Clear, flat control flow
- **No anonymous functions** - Named functions for clarity
- **Builder pattern** - Consistent, composable APIs
- **Type safety** - `Result` types force error handling
- **Quality testing** - Comprehensive test coverage

## About Dream

This module was originally built for the [Dream](https://github.com/TrustBound/dream) web toolkit, but it's completely standalone and can be used in any Gleam project. It follows Dream's design principles and will be maintained as part of the Dream ecosystem.

## License

MIT License - see LICENSE file for details.