slug.web.response
slug.web.response — HTTP response construction and formatting
Provides the Response and Reply structs, constructor helpers for all common HTTP status codes, header management, cookie support, HTMX helpers, and HTTP wire-format serialisation.
Quick start
val { ok, html, jsonOk, notFound, redirect, withHeader } = import("slug.web.response")
// simple text response
ok("Hello, World!")
// HTML response
html("<h1>Hello</h1>")
// JSON response
jsonOk({ name: "Alice", age: 30 })
// chaining modifiers
ok("done")
/> withHeader("x-custom", "value")
/> noCache
Response struct
Response{ status, headers, body } — header names are always lowercase. Use withHeader, withHeaders, addHeader, and withoutHeader to manage headers rather than constructing the map directly.
Reply struct
Reply is a higher-level descriptor used with the views layer. It carries a view name, optional HTMX fragment name, template data, status code, and extra headers. Pass a Reply to renderReply in slug.web.views to produce a Response.
Cookie options
The cookie function accepts an opts map with optional keys: path, domain, maxAge, expires, secure (bool), httpOnly (bool), sameSite ("Lax" | "Strict" | "None").
HTMX
hxTrigger, hxRedirect, and hxRetarget add the corresponding HTMX response headers (HX-Trigger, HX-Redirect, HX-Retarget).
TOC
- Reply
- Response
accepted(body)addHeader(res, key, value)badRequest(body)body(res)cacheSeconds(res, seconds)clearCookie(res, name, opts)conflict(body)cookie(name, value, opts)created(body)forbidden(body)formatHead(res)formatResponse(res)hasHeader(res, key)headers(res)html(markup)hxRedirect(res, location)hxRetarget(res, selector)hxTrigger(res, eventName)isFormattedResponse(x)isResponse(x)jsonOk(value)noCache(res)noContent()notFound(body)ok(body)payloadTooLarge(body)redirect(location)redirectPermanent(location)renderResponse(res)response(status, body)serverError(body)setCookie(res, name, value, opts)status(res)text(txt)unauthorized(body)withBody(res, body)withConnClose(res)withConnKeepAlive(res)withContentType(res, ct)withHeader(res, key, value)withHeaders(res, headersMap)withStatus(res, status)withoutHeader(res, key)
Structs
Reply
struct slug.web.response#Reply{view:str, fragment:str, data:map = {}, status:num = 200, headers:map = {}}
higher-level view descriptor for use with slug.web.views#renderReply.
Set fragment to render an HTMX partial instead of the full page. Extra headers are merged into the final response.
| Field | Type | Default | Description |
|---|---|---|---|
view | str | — | |
fragment | str | — | |
data | map | {} | |
status | num | 200 | |
headers | map | {} |
Response
struct slug.web.response#Response{status:num, headers:map, body}
raw HTTP response value. Construct via the helper functions, not directly.
Header names are always normalised to lowercase.
| Field | Type | Default | Description |
|---|---|---|---|
status | num | — | |
headers | map | — | |
body | — |
Functions
accepted(body)
fn slug.web.response#accepted(body:str):Response
202 Accepted response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
addHeader(res, key, value)
fn slug.web.response#addHeader(res, key:str, value:str):Response
adds a header value without replacing existing values for the same key.
Upgrades a single string value to a list when a second value is added. This is the correct way to set multiple Set-Cookie headers.
| Parameter | Type | Default |
|---|---|---|
res | — | |
key | str | — |
value | str | — |
badRequest(body)
fn slug.web.response#badRequest(body:str):Response
400 Bad Request response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
body(res)
fn slug.web.response#body(res):any
returns the body of a response.
| Parameter | Type | Default |
|---|---|---|
res | — |
cacheSeconds(res, seconds)
fn slug.web.response#cacheSeconds(res, seconds:num):Response
sets Cache-Control: public, max-age=<seconds>.
| Parameter | Type | Default |
|---|---|---|
res | — | |
seconds | num | — |
clearCookie(res, name, opts)
fn slug.web.response#clearCookie(res, name:str, opts:map):Response
expires a cookie immediately by setting Max-Age=0.
| Parameter | Type | Default |
|---|---|---|
res | — | |
name | str | — |
opts | map | — |
conflict(body)
fn slug.web.response#conflict(body:str):Response
409 Conflict response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
cookie(name, value, opts)
fn slug.web.response#cookie(name:str, value:str, opts:map):str
builds a Set-Cookie header value string.
opts may contain: path, domain, maxAge, expires, secure (bool), httpOnly (bool), sameSite ("Lax"|"Strict"|"None").
| Parameter | Type | Default |
|---|---|---|
name | str | — |
value | str | — |
opts | map | — |
created(body)
fn slug.web.response#created(body:str):Response
201 Created response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
forbidden(body)
fn slug.web.response#forbidden(body:str):Response
403 Forbidden response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
formatHead(res)
fn slug.web.response#formatHead(res):str
formats the response head (status line + headers) as an HTTP/1.1 string.
Automatically adds Content-Length if not already present.
| Parameter | Type | Default |
|---|---|---|
res | — |
formatResponse(res)
fn slug.web.response#formatResponse(res):FormattedResponse
formats a response into a FormattedResponse{ head, body }.
Automatically adds Content-Length if not already present.
| Parameter | Type | Default |
|---|---|---|
res | — |
hasHeader(res, key)
fn slug.web.response#hasHeader(res, key):bool
returns true if the response has a header with the given key.
Key comparison is case-insensitive.
| Parameter | Type | Default |
|---|---|---|
res | — | |
key | — |
headers(res)
fn slug.web.response#headers(res):map
returns the headers map of a response.
| Parameter | Type | Default |
|---|---|---|
res | — |
html(markup)
fn slug.web.response#html(markup:str):Response
200 OK response with Content-Type: text/html; charset=utf-8.
| Parameter | Type | Default |
|---|---|---|
markup | str | — |
hxRedirect(res, location)
fn slug.web.response#hxRedirect(res, location:str):Response
sets the HX-Redirect response header for a client-side redirect.
| Parameter | Type | Default |
|---|---|---|
res | — | |
location | str | — |
hxRetarget(res, selector)
fn slug.web.response#hxRetarget(res, selector:str):Response
sets the HX-Retarget response header to override the HTMX swap target.
| Parameter | Type | Default |
|---|---|---|
res | — | |
selector | str | — |
hxTrigger(res, eventName)
fn slug.web.response#hxTrigger(res, eventName:str):Response
sets the HX-Trigger response header to trigger a client-side event.
| Parameter | Type | Default |
|---|---|---|
res | — | |
eventName | str | — |
isFormattedResponse(x)
fn slug.web.response#isFormattedResponse(x):bool
returns true if x is a FormattedResponse struct instance.
| Parameter | Type | Default |
|---|---|---|
x | — |
isResponse(x)
fn slug.web.response#isResponse(x):bool
returns true if x is a Response struct instance.
| Parameter | Type | Default |
|---|---|---|
x | — |
jsonOk(value)
fn slug.web.response#jsonOk(value):Response
200 OK response with JSON-encoded value and Content-Type: application/json.
| Parameter | Type | Default |
|---|---|---|
value | — |
noCache(res)
fn slug.web.response#noCache(res):Response
sets headers to prevent all caching.
| Parameter | Type | Default |
|---|---|---|
res | — |
noContent()
fn slug.web.response#noContent():Response
204 No Content response.
notFound(body)
fn slug.web.response#notFound(body:str = "404 Not Found"):Response
404 Not Found response.
| Parameter | Type | Default |
|---|---|---|
body | str | "404 Not Found" |
ok(body)
fn slug.web.response#ok(body:str):Response
200 OK response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
payloadTooLarge(body)
fn slug.web.response#payloadTooLarge(body:str = "413 Payload Too Large"):Response
413 Payload Too Large response. Also sets Connection: close.
| Parameter | Type | Default |
|---|---|---|
body | str | "413 Payload Too Large" |
redirect(location)
fn slug.web.response#redirect(location:str):Response
302 Found redirect to location.
| Parameter | Type | Default |
|---|---|---|
location | str | — |
redirectPermanent(location)
fn slug.web.response#redirectPermanent(location:str):Response
301 Moved Permanently redirect to location.
| Parameter | Type | Default |
|---|---|---|
location | str | — |
renderResponse(res)
fn slug.web.response#renderResponse(res):str
renders a response to a complete HTTP/1.1 wire-format string (head + body).
| Parameter | Type | Default |
|---|---|---|
res | — |
response(status, body)
fn slug.web.response#response(status:num, body:str):Response
constructs a Response with the given status and body.
| Parameter | Type | Default |
|---|---|---|
status | num | — |
body | str | — |
serverError(body)
fn slug.web.response#serverError(body:str = "500 Server Error"):Response
500 Internal Server Error response.
| Parameter | Type | Default |
|---|---|---|
body | str | "500 Server Error" |
setCookie(res, name, value, opts)
fn slug.web.response#setCookie(res, name:str, value:str, opts:map):Response
adds a Set-Cookie header to res using cookie(name, value, opts).
Multiple cookies can be set by calling setCookie repeatedly — addHeader semantics ensure values are not clobbered.
| Parameter | Type | Default |
|---|---|---|
res | — | |
name | str | — |
value | str | — |
opts | map | — |
status(res)
fn slug.web.response#status(res):num
returns the status code of a response.
| Parameter | Type | Default |
|---|---|---|
res | — |
text(txt)
fn slug.web.response#text(txt:str):Response
200 OK response with Content-Type: text/plain; charset=utf-8.
| Parameter | Type | Default |
|---|---|---|
txt | str | — |
unauthorized(body)
fn slug.web.response#unauthorized(body:str):Response
401 Unauthorized response.
| Parameter | Type | Default |
|---|---|---|
body | str | — |
withBody(res, body)
fn slug.web.response#withBody(res, body:str):Response
returns a new response with body replaced.
| Parameter | Type | Default |
|---|---|---|
res | — | |
body | str | — |
withConnClose(res)
fn slug.web.response#withConnClose(res):Response
sets Connection: close.
| Parameter | Type | Default |
|---|---|---|
res | — |
withConnKeepAlive(res)
fn slug.web.response#withConnKeepAlive(res):Response
sets Connection: keep-alive.
| Parameter | Type | Default |
|---|---|---|
res | — |
withContentType(res, ct)
fn slug.web.response#withContentType(res, ct:str):Response
sets the content-type header.
| Parameter | Type | Default |
|---|---|---|
res | — | |
ct | str | — |
withHeader(res, key, value)
fn slug.web.response#withHeader(res, key:str, value:str):Response
returns a new response with key set to value, replacing any existing value.
Header keys are normalised to lowercase.
| Parameter | Type | Default |
|---|---|---|
res | — | |
key | str | — |
value | str | — |
withHeaders(res, headersMap)
fn slug.web.response#withHeaders(res, headersMap:map):Response
returns a new response with all headers from headersMap applied.
Each key is normalised to lowercase. Existing headers with the same key are replaced.
| Parameter | Type | Default |
|---|---|---|
res | — | |
headersMap | map | — |
withStatus(res, status)
fn slug.web.response#withStatus(res, status:num):Response
returns a new response with status replaced.
| Parameter | Type | Default |
|---|---|---|
res | — | |
status | num | — |
withoutHeader(res, key)
fn slug.web.response#withoutHeader(res, key:str):Response
returns a new response with key removed from headers.
| Parameter | Type | Default |
|---|---|---|
res | — | |
key | str | — |