libero/ssr

Helpers for server-side rendering with Libero.

Server-side: call a dispatch handler directly, encode flags for the HTML document, and render the full page shell.

Client-side: read and decode flags embedded by the server.

Types

pub type SsrError {
  BadResponse
  DispatchError
  BadFlags
}

Constructors

  • BadResponse
  • DispatchError
  • BadFlags

Values

pub fn boot_script(
  client_module client_module: String,
  flags flags: a,
) -> element.Element(msg)

Render a fragment of two <script> elements that boot the client app: one assigns the base64-encoded ETF flags to window.__LIBERO_FLAGS__, the other imports client_module as an ES module and calls main().

Drop this in your document tree (typically at the end of <body>) when building a server-rendered page.

html.body([], [
  html.div([attribute.id("app")], [views.view(model)]),
  ssr.boot_script(client_module: "/web/app.mjs", flags: model),
])

client_module is a JS import path controlled by the developer, not user input. It is concatenated into the generated <script type="module"> without escaping. If you derive this value from external input, you must validate it yourself.

pub fn call(
  handle handle: fn(state, BitArray) -> #(
    BitArray,
    option.Option(error.PanicInfo),
    state,
  ),
  handler_ctx handler_ctx: state,
  module module: String,
  msg msg: msg,
  expect expect: fn(response) -> payload,
) -> Result(payload, SsrError)

Call a dispatch handler directly on the server, returning a decoded payload. Encodes the call envelope, invokes the handler, strips the wire framing, and passes the response through the expect function to extract the desired value.

The response is the handler’s return type (e.g. Result(Int, Nil)).

ssr.call(
  handle: dispatch.handle,
  handler_ctx:,
  module: "rpc",
  msg: GetCounter,
  expect: fn(resp) {
    let assert Ok(n) = resp
    n
  },
)
// Returns Result(Int, SsrError)
pub fn decode_flags(
  flags: dynamic.Dynamic,
) -> Result(a, SsrError)

Decode flags from a Dynamic value (base64 ETF string). Use this in a Lustre init function to decode server-embedded flags.

Delegates to libero/ssr_decode, which avoids the mist import that pulls gramps/gleam_crypto into the browser build. Prefer importing libero/ssr_decode directly in client code.

pub fn encode_flags(data: a) -> String

Encode a value as a base64 ETF string, ready to embed in HTML as client flags.

pub fn handle_request(
  req req: request.Request(body),
  parse parse: fn(uri.Uri) -> Result(route, Nil),
  load load: fn(request.Request(body), route, state) -> Result(
    model,
    response.Response(mist.ResponseData),
  ),
  render render: fn(route, model) -> element.Element(msg),
  handler_ctx handler_ctx: state,
) -> response.Response(mist.ResponseData)

Render a server-side page for an HTTP request.

Pipeline: parse(uri) -> load(req, route, handler_ctx) -> render(route, model) -> HTML response.

  • Non-GET requests get a 405 Method Not Allowed.
  • parse returning Error(Nil) gets a bare 404 Not Found. Custom 404 pages: handle the catch-all in your mist router and only call handle_request for paths you recognize.
  • load returning Error(response) returns that exact response: the loader owns auth redirects, soft 404s with custom bodies, etc.
  • load returning Ok(model) renders the document tree from render into a 200 OK HTML response.
ssr.handle_request(
  req:,
  parse: router.parse_route,
  load: load_page,
  render: render_page,
  handler_ctx:,
)
Search Document