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:
examples/elixir_smoke.exsexamples/elixir_usage.exsexamples/gleam_smoke/src/datastar_beam_gleam_smoke.gleamexamples/gleam_smoke/src/datastar_beam_usage.gleam
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.