Skip to content

http

import http
FunctionSignatureDescription
serve(Int, (Unknown) -> Unknown) -> UnitStart an HTTP server on the given port with a request handler
response(Int, String) -> ResponseCreate a plain text HTTP response with status code
json(Int, String) -> ResponseCreate a JSON HTTP response with status code
with_headers(Int, String, Map[String, String]) -> ResponseCreate a response with custom headers
redirect(String) -> ResponseCreate a 302 temporary redirect response
status(Response, Int) -> ResponseSet the status code on a response
body(Response) -> StringGet the body string from a response
set_header(Response, String, String) -> ResponseSet a header on a response
get_header(Response, String) -> Option[String]Get a header value from a response (first occurrence)
status_code(Response) -> IntGet the status code of a response
headers(Response) -> Map[String, String]All headers as a lowercased-name map (first occurrence wins)
header_values(Response, String) -> List[String]Every occurrence of a header (e.g. set-cookie)
req_method(Request) -> StringGet the HTTP method of a request (GET, POST, etc.)
req_path(Request) -> StringGet the URL path of a request
req_body(Request) -> StringGet the body string of a request
req_header(Request, String) -> Option[String]Get a header value from a request
query_params(Request) -> Map[String, String]Get all query parameters from a request as a map
get(String) -> Result[String, String]Send an HTTP GET request and return the response body
post(String, String) -> Result[String, String]Send an HTTP POST request with a body string
put(String, String) -> Result[String, String]Send an HTTP PUT request
patch(String, String) -> Result[String, String]Send an HTTP PATCH request
delete(String) -> Result[String, String]Send an HTTP DELETE request
request(String, String, String, Map[String, String]) -> Result[String, String]Send a custom HTTP request with method, URL, body, and headers
get_response(String) -> Result[Response, String]Like get, but return the whole response (status, headers, body)
post_response(String, String) -> Result[Response, String]Like post, but return the whole response
put_response(String, String) -> Result[Response, String]Like put, but return the whole response
patch_response(String, String) -> Result[Response, String]Like patch, but return the whole response
delete_response(String) -> Result[Response, String]Like delete, but return the whole response
request_response(String, String, String, Map[String, String]) -> Result[Response, String]Like request, but return the whole response

http.serve(port: Int, f: (Unknown) -> Unknown) -> Unit

Section titled “http.serve(port: Int, f: (Unknown) -> Unknown) -> Unit”

Start an HTTP server on the given port with a request handler

http.serve(3000, (req) => http.response(200, "ok"))

http.response(status: Int, body: String) -> Response

Section titled “http.response(status: Int, body: String) -> Response”

Create a plain text HTTP response with status code

http.response(200, "Hello!")

http.json(status: Int, body: String) -> Response

Section titled “http.json(status: Int, body: String) -> Response”

Create a JSON HTTP response with status code

http.json(200, json.stringify(data))

http.with_headers(status: Int, body: String, headers: Map[String, String]) -> Response

Section titled “http.with_headers(status: Int, body: String, headers: Map[String, String]) -> Response”

Create a response with custom headers

http.with_headers(200, body, {"Content-Type": "text/html"})

Create a 302 temporary redirect response

http.redirect("/new-path")

http.status(resp: Response, code: Int) -> Response

Section titled “http.status(resp: Response, code: Int) -> Response”

Set the status code on a response

http.status(resp, 201)

Get the body string from a response

let text = http.body(resp)

http.set_header(resp: Response, key: String, value: String) -> Response

Section titled “http.set_header(resp: Response, key: String, value: String) -> Response”

Set a header on a response

http.set_header(resp, "X-Custom", "value")

http.get_header(resp: Response, key: String) -> Option[String]

Section titled “http.get_header(resp: Response, key: String) -> Option[String]”

Get a header value from a response

let ct = http.get_header(resp, "Content-Type")

Get the HTTP method of a request (GET, POST, etc.)

let method = http.req_method(req)

Get the URL path of a request

let path = http.req_path(req)

Get the body string of a request

let body = http.req_body(req)

http.req_header(req: Request, key: String) -> Option[String]

Section titled “http.req_header(req: Request, key: String) -> Option[String]”

Get a header value from a request

let auth = http.req_header(req, "Authorization")

http.query_params(req: Request) -> Map[String, String]

Section titled “http.query_params(req: Request) -> Map[String, String]”

Get all query parameters from a request as a map

let params = http.query_params(req) // {"page": "1", "q": "test"}

http.get(url: String) -> Result[String, String]

Section titled “http.get(url: String) -> Result[String, String]”

Send an HTTP GET request and return the response body

let html = http.get("https://example.com")!

http.post(url: String, body: String) -> Result[String, String]

Section titled “http.post(url: String, body: String) -> Result[String, String]”

Send an HTTP POST request with a body string

let resp = http.post("https://api.example.com", body)!

http.put(url: String, body: String) -> Result[String, String]

Section titled “http.put(url: String, body: String) -> Result[String, String]”

Send an HTTP PUT request

let resp = http.put(url, body)!

http.patch(url: String, body: String) -> Result[String, String]

Section titled “http.patch(url: String, body: String) -> Result[String, String]”

Send an HTTP PATCH request

let resp = http.patch(url, body)!

http.delete(url: String) -> Result[String, String]

Section titled “http.delete(url: String) -> Result[String, String]”

Send an HTTP DELETE request

let resp = http.delete(url)!

http.request(method: String, url: String, body: String, headers: Map[String, String]) -> Result[String, String]

Section titled “http.request(method: String, url: String, body: String, headers: Map[String, String]) -> Result[String, String]”

Send a custom HTTP request with method, URL, body, and headers

let resp = http.request("PUT", url, body, headers)!

The *_response family — status, headers and body together

Section titled “The *_response family — status, headers and body together”

Every verb-shaped String client has a *_response twin with the same parameters that returns the whole Response record instead of just the body:

body onlyfull response
http.get(url)http.get_response(url)
http.post(url, body)http.post_response(url, body)
http.put(url, body)http.put_response(url, body)
http.patch(url, body)http.patch_response(url, body)
http.delete(url)http.delete_response(url)
http.request(method, url, body, headers)http.request_response(method, url, body, headers)

The body-only function is the body projection of its twin, so the two never disagree. get_bytes / request_bytes / get_status / request_status are result-shape variants, not verbs, and have no twin.

What the record holds:

  • http.status_code(resp) — any complete response is ok, a 404 and a 3xx included. err is a transport failure only (connection / TLS / timeout).
  • Redirects are never followed. A 3xx arrives as-is with its Location header, so the response is always to the URL you passed.
  • Headers keep their wire spelling and a repeated field keeps every occurrence: http.get_header(resp, k) answers the first, http.header_values(resp, k) all of them, http.headers(resp) the lowercased-name map (first wins).
  • http.body(resp) — the transfer-decoded body text.

The twins are native-only today: the embedded wasm lane serves the body / status / bytes shapes and grows the response shape when the wasi:http port lands.

import http
effect fn main() -> Unit = {
let resp = http.get_response("https://example.com/")!
println("status ${int.to_string(http.status_code(resp))}")
println(http.get_header(resp, "content-type") ?? "no Content-Type")
for cookie in http.header_values(resp, "set-cookie") {
println("cookie: ${cookie}")
}
println(http.body(resp))
}

Get the status code of a response. A 3xx or 4xx is still an ok response.

let code = http.status_code(resp)

http.headers(resp: Response) -> Map[String, String]

Section titled “http.headers(resp: Response) -> Map[String, String]”

All headers as a map keyed by lowercased name; for a repeated field the first occurrence wins.

let ct = map.get(http.headers(resp), "content-type")

http.header_values(resp: Response, key: String) -> List[String]

Section titled “http.header_values(resp: Response, key: String) -> List[String]”

Every occurrence of a header, in wire order — what you want for set-cookie.

let cookies = http.header_values(resp, "set-cookie")

http.get_response(url: String) -> Result[Response, String]

Section titled “http.get_response(url: String) -> Result[Response, String]”
let resp = http.get_response("https://example.com")!

http.post_response(url: String, body: String) -> Result[Response, String]

Section titled “http.post_response(url: String, body: String) -> Result[Response, String]”
let resp = http.post_response("https://api.example.com/login", body)!

http.put_response(url: String, body: String) -> Result[Response, String]

Section titled “http.put_response(url: String, body: String) -> Result[Response, String]”
let resp = http.put_response(url, body)!

http.patch_response(url: String, body: String) -> Result[Response, String]

Section titled “http.patch_response(url: String, body: String) -> Result[Response, String]”
let resp = http.patch_response(url, body)!

http.delete_response(url: String) -> Result[Response, String]

Section titled “http.delete_response(url: String) -> Result[Response, String]”
let resp = http.delete_response(url)!

http.request_response(method: String, url: String, body: String, headers: Map[String, String]) -> Result[Response, String]

Section titled “http.request_response(method: String, url: String, body: String, headers: Map[String, String]) -> Result[Response, String]”

The general form. A redirect check reads the 3xx and its Location straight off the record:

import http
effect fn main() -> Unit = {
let resp = http.request_response("GET", "http://example.com/old", "", ["User-Agent": "checker"])!
let code = http.status_code(resp)
if code >= 300 and code < 400 then
println("redirects to ${http.get_header(resp, "location") ?? "?"}")
else
println("answers ${int.to_string(code)} directly")
}