Extracting Datastar Signals from Requests
When the client sends a request to the server, it includes the current values of all signals. For GET requests, signals are sent in a special query parameter named datastar; for non-GET requests (POST, PUT, etc.), signals are sent in the request body. You can extract these signals from the request using readSignals[T].
Example: Form with Signal Binding
For example, the following HTML form binds an input field to a signal named delay; when the form is submitted, the current value of the delay signal is sent to the server:
import zio.http.template2._
import zio.http.datastar._
import zio.http.endpoint.Endpoint
import zio.http._
body(
h1("Hello World Example"), {
val $delay = Signal[Int]("delay")
div(
dataSignals($delay) := js"100",
label("Delay (ms): ", `for` := "delay"),
input(dataBind($delay.name), name := "delay", `type` := "number", step := "100"),
)
},
button(dataOn.click := Endpoint(Method.GET / "server-time").out[String].datastarRequest(()))("Start Animation"),
div(id("message")),
)
Reading Signals on the Server
The server can extract the signals from the request using the readSignals[T] method:
import zio._
import zio.schema._
import zio.http._
import zio.http.template2._
import zio.http.datastar._
case class Delay(value: Int)
object Delay {
implicit val schema: Schema[Delay] = DeriveSchema.gen
}
val route =
Method.GET / "hello-world" -> events {
handler { (request: Request) =>
for {
// Extract delay once before the loop to avoid repeated JSON decoding
delay <- request.readSignals[Delay].orElse(ZIO.succeed(Delay(100)))
message = "Hello, world!"
_ <- ZIO.foreachDiscard(message.indices) { i =>
for {
_ <- ServerSentEventGenerator.executeScript(js"console.log('Sending substring(0, ${i + 1})')")
_ <- ServerSentEventGenerator.patchElements(div(id("message"), message.substring(0, i + 1)))
_ <- ZIO.sleep(delay.value.millis)
} yield ()
}
} yield ()
}
}
How It Works
- The client declares signals using the
dataSignalsattribute - Input fields are bound to signals using the
dataBindattribute - When a request is made (via
dataOnevent listeners or form submission), Datastar automatically includes all current signal values in thedatastarquery parameter as JSON - On the server, you define a case class that matches the signal structure
- Use
request.readSignals[T]to deserialize and type-safely extract the signal values
The case class fields should match your signal names exactly. ZIO's Schema derivation automatically handles the deserialization.