Datastar BEAM SDK

The datastar_beam package provides the datastar-beam SDK core for working with Datastar.

Datastar sends responses back to the browser using Server-Sent Events. This allows a BEAM backend to send any number of events, from zero to infinity, in response to a single request.

datastar_beam has helpers for creating those events, formatting Datastar patches, reading signals from the frontend, executing scripts, redirecting the browser, and generating Datastar frontend action expressions. It is written in portable Erlang, so Erlang, Elixir, Gleam, LFE, and other BEAM languages can call the same module.

This package is intentionally web-server-neutral. It does not own sockets, request objects, response streams, routes, or framework adapters. Cowboy, Phoenix, Plug, Mist, Elli, and other adapters can use the SDK core by writing the returned iodata to an SSE response.

License

This package is licensed for free under the MIT License.

Requirements

This package requires Erlang/OTP 27 or later. The current implementation uses OTP's built-in json module.

Runtime dependencies are limited to kernel and stdlib.

Installation

Rebar3:

{deps, [
{datastar_beam, "0.1.0"}
]}.

Mix:

def deps do
[
{:datastar_beam, "~> 0.1.0"}
]
end

Gleam:

gleam add datastar_beam

Hex, OTP application, and Erlang module identifiers use datastar_beam, the BEAM-safe spelling of the datastar-beam project name.

Usage

The SDK functions return iodata. Your web framework or adapter should set the SSE response headers, then stream or write the events returned by datastar_beam.

handler(Method, QueryString, Body) ->
{ok, Signals} = datastar_beam:read_signals(Method, QueryString, Body),
Count = maps:get(<<"count">>, Signals, 0),
Headers = datastar_beam:sse_headers(),
Events = [
datastar_beam:patch_elements(
<<"<div id=\"message\">Hello from Datastar on the BEAM!</div>">>,
#{selector => <<"#message">>}
),
datastar_beam:remove_elements(<<"#temporary-element">>),
datastar_beam:patch_signals(#{
<<"message">> => <<"Updated message">>,
<<"count">> => Count + 1
}),
datastar_beam:execute_script(<<"console.log('Hello from server!')">>),
datastar_beam:redirect(<<"/new-page">>)
],
{200, Headers, Events}.

Elixir can call the Erlang module directly:

event =
:datastar_beam.patch_signals(%{
"message" => "Hello from Elixir"
})
IO.puts(IO.iodata_to_binary(event))

Gleam can bind the Erlang module with @external:

import gleam/dynamic.{type Dynamic}
import gleam/io
@external(erlang, "datastar_beam", "patch_signals")
fn patch_signals(signals: String) -> Dynamic
@external(erlang, "erlang", "iolist_to_binary")
fn iolist_to_binary(iodata: Dynamic) -> String
pub fn main() {
patch_signals("{\"message\":\"Hello from Gleam\"}")
|> iolist_to_binary
|> io.println
}

All event builders produce Datastar-compatible SSE bytes:

event: datastar-patch-signals
data: signals {"message":"Hello from BEAM"}

Event Generation Helpers

The core helper is event/2,3, which formats arbitrary SSE events. The Datastar-specific helpers build on top of it:

patch_elements/1,2
remove_elements/1,2
patch_signals/1,2
remove_signals/1,2
execute_script/1,2
redirect/1,2
console_log/1,2

HTML inputs may be iodata, binaries, strings, {safe, Iodata}, or {ok, Iodata}. Options may be Erlang maps or proplists, which keeps Elixir keyword lists and plain BEAM callers pleasant.

Response Helpers

sse_headers/0 returns the headers recommended for Datastar SSE responses:

datastar_beam:sse_headers().

The SDK does not create a response object because each BEAM web stack streams in its own way. Adapter packages should own request and response integration while passing rendered HTML, JSON signals, and scripts through this module.

Signal Helpers

The current state of Datastar signals is included in Datastar requests. read_signals/3 decodes those signals from the request parts supplied by your adapter:

datastar_beam:read_signals(
get,
<<"datastar=%7B%22count%22%3A41%7D">>,
<<>>
).

For GET and DELETE requests, signals are read from the datastar query parameter. For other request methods, the body is decoded as JSON.

Attribute And Action Helpers

Datastar allows server-rendered HTML to declare frontend behavior using data-* attributes. This SDK provides action-expression helpers that can be used from templates or component functions:

datastar_beam:post(<<"/counter/increment">>).
datastar_beam:delete(<<"/items/42">>, #{
options => <<"{retryMaxCount: Infinity}">>
}).

The SDK stays template-engine-neutral. Render HTML with HEEx, Nakai, ErlyDTL, Phoenix components, or another template library first; then pass the rendered iodata to patch_elements/1,2.

Examples

See the examples directory for working BEAM examples:

Additional notes are available in:

Testing

rebar3 eunit
rebar3 compile
./test.sh

./test.sh also runs the Elixir and Gleam examples when those tools are available on PATH.

Contributing

Contributions are welcome. Please keep the SDK core portable across BEAM languages and free of web-server-specific dependencies.