howdy/stream

Response bodies written a piece at a time, so each piece reaches the client as soon as it is ready: a large CSV export, a log being tailed, the tokens of a language model as they arrive.

import howdy/stream

controller.get("/users.csv", fn(_ctx) {
  response.new(200)
  |> response.set_header("content-type", "text/csv")
  |> stream.body(fn(writer) {
    use writer <- result.try(stream.send_text(writer, "id,name\n"))
    list.try_fold(users(), writer, fn(writer, user) {
      stream.send_text(writer, user.id <> "," <> user.name <> "\n")
    })
  })
})

The function runs once the headers have gone, in the connection’s own process. Each send hands back the writer for the next one. Returning Ok ends the body. Returning Error stops writing and closes the connection without ending the body, so the client can tell it was cut short; the error is logged unless it came from a send that failed because the client had gone.

For messages pushed to a browser over time, use howdy/sse instead. In tests, howdy/testing.text and testing.bytes read a streamed body in full.

Types

Where the pieces of a streamed body are written. Given to the function passed to body.

pub opaque type Writer

Values

pub fn body(
  response: response.Response(a),
  write: fn(Writer) -> Result(Writer, e),
) -> response.Response(content.Content)

Set the body of response to one written by write, a piece at a time. Any body already set is replaced.

pub fn send(
  writer: Writer,
  bits: BitArray,
) -> Result(Writer, content.SendError)

Send bits to the client now.

pub fn send_text(
  writer: Writer,
  text: String,
) -> Result(Writer, content.SendError)

Send text to the client now, as UTF-8.

✨ Search Document