Current section
Files
Jump to
Current section
Files
src/lustre_http.gleam
import gleam/json
import lustre/cmd.{Cmd}
pub type HttpError {
Unauthorized
NotFound
InternalServerError(String)
OtherError(Int, String)
}
pub type StringResult =
Result(String, HttpError)
pub type HttpOrJsonError {
H(HttpError)
J(json.DecodeError)
}
pub type JsonResult(t) =
Result(t, HttpOrJsonError)
external fn do_request(
method: String,
url: String,
body: String,
on_success: fn(String) -> Nil,
on_error: fn(Int, String) -> Nil,
) -> Nil =
"./ffi.mjs" "do_request"
fn do_get(url, on_success, on_error) {
do_request("GET", url, "", on_success, on_error)
}
fn do_post(url, body, on_success, on_error) {
do_request("POST", url, body, on_success, on_error)
}
/// ### Usage
/// ```
/// import lustre_http as http
///
/// type Msg {
/// SomeInteraction
/// TextReceived(StringResult)
/// }
///
/// pub fn update(model, msg) {
/// case msg {
/// SomeInteraction -> #(model, http.get_as_text("the_text", TextReceived))
/// TextReceived(Ok(text)) -> #(apply(text, model), cmd.none())
/// TextReceived(Error(e)) -> #(indicate_problem(e, model), cmd.none())
/// }
/// }
/// ```
pub fn get_as_text(url: String, to_msg: fn(StringResult) -> msg) -> Cmd(msg) {
cmd.from(fn(dispatch) {
do_get(
url,
fn(body) { dispatch(to_msg(Ok(body))) },
fn(code, msg) {
decode_error(code, msg)
|> Error
|> to_msg
|> dispatch
},
)
})
}
fn decode_error(code, msg) {
case code {
401 -> Unauthorized
404 -> NotFound
500 -> InternalServerError(msg)
_ -> OtherError(code, msg)
}
}
/// Will automatically try to decode the JSON for you.
/// The error-type is a bit complicated. In a future version will probably
/// (a) force "text/json" in the headers (b) not decode for you.
pub fn get_as_json(
url: String,
to_msg: fn(JsonResult(String)) -> msg,
decoder,
) -> Cmd(msg) {
cmd.from(fn(dispatch) {
do_get(
url,
fn(body) {
case json.decode(from: body, using: decoder) {
Ok(json) -> dispatch(to_msg(Ok(json)))
Error(json_error) -> dispatch(to_msg(Error(J(json_error))))
}
},
fn(code, msg) {
let http_error = case code {
401 -> Unauthorized
404 -> NotFound
500 -> InternalServerError(msg)
_ -> OtherError(code, msg)
}
dispatch(to_msg(Error(H(http_error))))
},
)
})
}
/// Future versions will force headers in both the request and the response
/// so you can post json and receive text, post text and receive json, etc.
pub fn post_text(url: String, body: String, to_msg: fn(StringResult) -> msg) {
cmd.from(fn(dispatch) {
do_post(
url,
body,
fn(body) { dispatch(to_msg(Ok(body))) },
fn(code, msg) {
decode_error(code, msg)
|> Error
|> to_msg
|> dispatch
},
)
})
}