glimr/response/response
HTTP Response Helpers
Wisp’s response API is low-level — building a JSON response means serializing, setting headers, and wrapping in a response struct manually. Controllers that repeat this for every endpoint accumulate boilerplate that obscures what the handler is actually trying to return. These helpers reduce each response type to a one-liner so controllers stay focused on the data, not the encoding.
Types
The error handler middleware needs to know whether to render
an HTML error page or a JSON {"error": "..."}, and
checking Accept headers at every call site would be tedious
and error-prone. Storing the format as a typed enum on the
request context lets downstream code branch cleanly with a
single pattern match.
pub type ResponseFormat {
HTML
JSON
}
Constructors
-
HTML -
JSON
Values
pub fn empty(status: Int) -> response.Response(wisp.Body)
Some responses don’t need a body — a 204 after a successful DELETE, a 401 rejection, a 301 redirect where only headers matter. Forcing controllers to build an html or json response in those cases just adds noise.
Example:
response.empty(401)
pub fn error(status: Int) -> response.Response(wisp.Body)
Apps should be able to brand their error pages without modifying framework code. Checking the app’s views/errors/ directory first lets developers drop in a custom 404.html or 500.html that takes priority, while the framework’s built-in generic page serves as a sensible default until custom ones are created.
Example:
// Returns 404 response with custom or default error page
response.error(404)
// Returns 500 response with custom or default error page
response.error(500)
pub fn header(
response: response.Response(wisp.Body),
key: String,
value: String,
) -> response.Response(wisp.Body)
Lets controllers chain headers with the pipe operator without importing wisp. This matters because once you import wisp in a controller for headers, it’s tempting to use wisp’s response functions directly too — and then the abstraction boundary this module provides falls apart.
Example:
response.html("\"This is actually json\"", 200)
|> response.header("content-type", "application/json")
pub fn html(
content: String,
status: Int,
) -> response.Response(wisp.Body)
Controllers shouldn’t need to import wisp just to return some HTML. Keeping all response constructors in this module means the underlying HTTP library is only referenced here — if we ever swap wisp for something else, controllers don’t change at all.
Example:
let html = "<h1>Hello World</h1>"
response.html(html, 200)
pub fn html_file(
file_path: String,
status: Int,
) -> response.Response(wisp.Body)
Loading HTML from a file keeps large page content out of Gleam source code, which is cleaner for big pages and lets designers edit templates without touching application logic. The assert panics on missing files immediately rather than serving an empty response — a typo in a template path shows up the first time you hit the route, not as a silent blank page in production.
Example:
response.html_file("contact/success.html", 200)
pub fn internal_server_error() -> response.Response(wisp.Body)
When something genuinely breaks — a database write fails, an external service is down — controllers need a clean way to bail out. Like the other status helpers, this keeps wisp imports out of controller code so the abstraction boundary holds.
pub fn json(
json: json.Json,
status: Int,
) -> response.Response(wisp.Body)
Serializing and setting the content-type in one call prevents the common mistake of returning JSON with an HTML content type — which causes browsers and API clients to misparse the response body.
Example:
json.string("This is a json response!")
|> response.json(200)
json.object([
#("name", json.string("John Doe")),
#("email", json.string("johndoe@email.com")),
#("age", json.int(30)),
])
|> response.json(200)
pub fn method_not_allowed(
allowed: List(http.Method),
) -> response.Response(wisp.Body)
When a URL matches but the HTTP method doesn’t — say a POST to a GET-only route — the spec requires a 405 with an Allow header listing what methods actually work. The route compiler generates these automatically so developers get correct HTTP semantics without thinking about it.
pub fn not_found() -> response.Response(wisp.Body)
The route compiler’s generated dispatch code needs a 404 to return when no route matches, and controllers need one when a resource lookup comes up empty. Having it here keeps wisp out of both — and if we ever want 404s to go through the custom error page system, there’s one place to change.
pub fn status_reason(status: Int) -> String
Showing users “404” alone means nothing to most people, but “Not Found” immediately tells them what happened. The error handler middleware uses this for JSON error messages, and the generic error page uses it for HTML — keeping the lookup here means both formats always agree on what to call each status code.
pub const views_path: String
Every function that reads a view file — html_file, error, and anything else that loads templates — needs to agree on where views live. Putting the path here means changing the directory structure is a one-line fix instead of a find-and-replace across the codebase.