HTTP

The HTTP module provides a complete HTTP client for making web requests. It supports all standard HTTP methods, custom headers, Bearer token authentication, and automatic retry with exponential backoff.

Basic Requests

HTTP.get
String -> Result Response HTTPError
Performs an HTTP GET request to the specified URL.
match HTTP.get "https://api.example.com/users"
  | Ok response -> println response.body
  | Err e -> println "Error: ${e}"
HTTP.post
String -> String -> Result Response HTTPError
Performs an HTTP POST request with a body.
body = "{\"name\": \"Alice\", \"email\": \"alice@example.com\"}"

match HTTP.post "https://api.example.com/users" body
  | Ok response -> println "Created: ${response.body}"
  | Err e -> println "Error: ${e}"
HTTP.put
String -> String -> Result Response HTTPError
Performs an HTTP PUT request with a body.
HTTP.put "https://api.example.com/users/1" "{\"name\": \"Bob\"}"
HTTP.delete
String -> Result Response HTTPError
Performs an HTTP DELETE request.
HTTP.delete "https://api.example.com/users/1"
HTTP.patch
String -> String -> Result Response HTTPError
Performs an HTTP PATCH request with a body.
HTTP.patch "https://api.example.com/users/1" "{\"status\": \"active\"}"
HTTP.head
String -> Result Response HTTPError
Performs an HTTP HEAD request (returns headers only, no body).
HTTP.head "https://api.example.com/users"

Convenience Functions

For quick scripts where you want to panic on errors, use the bang variants:

HTTP.get!
String -> String
GET request that returns the body directly. Panics on error.
body = HTTP.get! "https://api.example.com/data"
println body
HTTP.post!
String -> String -> Response
POST request that returns the response directly. Panics on error.
response = HTTP.post! "https://api.example.com/data" "{\"key\": \"value\"}"
println response.body

Custom Headers

HTTP.get-with-headers
String -> a -> Result Response HTTPError
GET request with custom headers. Headers should be a record with string keys and values.
headers = {
  "Authorization": "Bearer my-token",
  "Content-Type": "application/json"
}

match HTTP.get-with-headers "https://api.example.com/protected" headers
  | Ok response -> println response.body
  | Err e -> println "Error: ${e}"
HTTP.post-with-headers
String -> String -> a -> Result Response HTTPError
POST request with custom headers.
match HTTP.post-with-headers "https://api.example.com/data" body headers
  | Ok response -> println response.body
  | Err e -> println "Error: ${e}"

Bearer Authentication

Convenience functions for Bearer token authentication, commonly used with OAuth2 and API tokens.

HTTP.bearer-auth
String -> String
Creates an Authorization header value from a token.
auth-header = HTTP.bearer-auth "my-token"
# Returns: "Bearer my-token"

# Use with custom headers
headers = {authorization: auth-header, "X-Custom": "value"}
HTTP.get-with-headers url headers
HTTP.get-with-bearer
String -> String -> Result Response HTTPError
GET request with Bearer token authentication.
api-token = "my-secret-token"

match HTTP.get-with-bearer "https://api.example.com/protected" api-token
  | Ok response -> println response.body
  | Err e -> println "Error: ${e}"
HTTP.post-with-bearer
String -> String -> String -> Result Response HTTPError
POST request with Bearer token authentication.
body = "{\"data\": \"value\"}"
match HTTP.post-with-bearer "https://api.example.com/data" body api-token
  | Ok response -> println response.body
  | Err e -> println "Error: ${e}"
HTTP.parse-bearer-token
String -> Option String
Extracts a Bearer token from an Authorization header value.
match HTTP.parse-bearer-token "Bearer abc123"
  | Some token -> println "Token: ${token}"
  | None -> println "No bearer token found"

# Returns None for non-Bearer auth or empty tokens
HTTP.parse-bearer-token "Basic xyz"   # None
HTTP.parse-bearer-token "Bearer "     # None (empty token)

Retry Support

HTTP.fetch-with-retry
String -> a -> Result Response HTTPError
Fetch with automatic retry and exponential backoff for handling transient failures.
options = {
  method: "GET",
  retry: {
    max-retries: 3,
    backoff: "exponential",
    base-delay-ms: 100
  }
}

match HTTP.fetch-with-retry "https://api.example.com/data" options
  | Ok response -> println response.body
  | Err e -> println "Failed after retries: ${e}"
HTTP.fetch
String -> a -> Result Response HTTPError
Generic fetch with full control over method, headers, body, and timeout.
HTTP.request
String -> a -> Result Response HTTPError
Low-level request function with full control over method and options.

Response Helpers

Convenience functions for checking HTTP response status codes.

HTTP.success?
Response -> Bool
Returns true if the response was successful (2xx status code).
HTTP.redirect?
Response -> Bool
Returns true if the response was a redirect (3xx status code).
HTTP.client-error?
Response -> Bool
Returns true if the response was a client error (4xx status code).
HTTP.server-error?
Response -> Bool
Returns true if the response was a server error (5xx status code).
match HTTP.get "https://api.example.com/data"
  | Ok response ->
      if HTTP.success? response then
        println "Success: ${response.body}"
      else if HTTP.redirect? response then
        println "Redirect (${response.status})"
      else if HTTP.client-error? response then
        println "Client error (${response.status})"
      else if HTTP.server-error? response then
        println "Server error (${response.status})"
      else
        println "Unknown status: ${response.status}"
  | Err e -> println "Error: ${e}"

Error Types

All HTTP functions return Result with HTTPError for typed error handling.

type HTTPError =
  | HTTPConnectionError { message: String }
  | HTTPTimeoutError { message: String }
  | HTTPRequestError { message: String }
  | HTTPResponseError { message: String }

Handling Errors

match HTTP.get "https://api.example.com/users"
  | Ok response -> println response.body
  | Err (HTTPConnectionError { message }) ->
      println "Connection failed: ${message}"
  | Err (HTTPTimeoutError { message }) ->
      println "Request timed out: ${message}"
  | Err (HTTPRequestError { message }) ->
      println "Request error: ${message}"
  | Err (HTTPResponseError { message }) ->
      println "Response error: ${message}"

# Or use the Error trait for generic handling
match HTTP.get "https://api.example.com/users"
  | Ok response -> println response.body
  | Err e -> println "Error (${Error.kind e}): ${Error.message e}"