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.

Network Capability

HTTP client helpers require TCPConnectAuth, TCPAuth, NetAuth, or RootAuth in scope. HTTP server helpers require TCPListenAuth, TCPAuth, NetAuth, or RootAuth.

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}"