| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Hypermedia.Datastar.WAI
Description
This module connects Datastar to WAI (Web Application Interface), Haskell's standard HTTP server interface. It provides:
sseResponse— create a streaming SSE response with aServerSentEventGeneratorcallbacksendPatchElements,sendPatchSignals,sendExecuteScript— send Datastar events through the open connectionreadSignals— decode signals sent by the browser (from query params on GET, or from the request body on POST)isDatastarRequest— distinguish Datastar SSE requests from normal page loads
Streaming architecture
sseResponse uses WAI's responseStream to hold the HTTP connection open.
You receive a ServerSentEventGenerator and call send* functions as many
times as needed. The connection stays open until your callback returns (or
the client disconnects).
Thread safety
The ServerSentEventGenerator uses an internal MVar
lock, so it is safe to call send* functions from multiple threads
concurrently.
How signals flow from browser to server
When the browser makes a Datastar request, signals are sent as JSON:
- GET requests: signals are URL-encoded in the
datastarquery parameter - POST requests: signals are in the request body as JSON
Use readSignals with any FromJSON instance to decode them.
Synopsis
- data ServerSentEventGenerator = ServerSentEventGenerator {}
- sseResponse :: DatastarLogger -> (ServerSentEventGenerator -> IO ()) -> Response
- send :: ServerSentEventGenerator -> DatastarEvent -> IO ()
- sendPatchElements :: ServerSentEventGenerator -> PatchElements -> IO ()
- sendPatchSignals :: ServerSentEventGenerator -> PatchSignals -> IO ()
- sendExecuteScript :: ServerSentEventGenerator -> ExecuteScript -> IO ()
- readSignals :: FromJSON a => Request -> IO (Either String a)
- parseFromQuery :: FromJSON a => Request -> Either String a
- parseFromBody :: FromJSON a => Request -> IO (Either String a)
- isDatastarRequest :: Request -> Bool
- renderEvent :: DatastarEvent -> Builder
Documentation
data ServerSentEventGenerator Source #
An opaque handle for sending SSE events to the browser.
Obtain one from the callback passed to sseResponse. The handle is
thread-safe — you can send events from multiple threads concurrently.
You don't construct these directly; sseResponse creates one for you.
Constructors
| ServerSentEventGenerator | |
sseResponse :: DatastarLogger -> (ServerSentEventGenerator -> IO ()) -> Response Source #
Create a WAI Response that streams SSE events.
The callback receives a ServerSentEventGenerator for sending events.
The SSE connection stays open until the callback returns.
app :: WAI.Request -> (WAI.Response -> IO b) -> IO b
app req respond =
respond $ sseResponse $ \gen -> do
sendPatchElements gen (patchElements "<div id=\"msg\">Hello</div>")
send :: ServerSentEventGenerator -> DatastarEvent -> IO () Source #
sendPatchElements :: ServerSentEventGenerator -> PatchElements -> IO () Source #
Send a PatchElements event, morphing HTML into the browser's DOM.
sendPatchSignals :: ServerSentEventGenerator -> PatchSignals -> IO () Source #
Send a PatchSignals event, updating the browser's reactive signal store.
sendExecuteScript :: ServerSentEventGenerator -> ExecuteScript -> IO () Source #
Send an ExecuteScript event, running JavaScript in the browser.
readSignals :: FromJSON a => Request -> IO (Either String a) Source #
Decode signals sent by the browser in a Datastar request.
For GET requests, signals are URL-encoded in the datastar query parameter.
For POST requests (and other methods), signals are read from the request
body as JSON.
Define a Haskell data type with a FromJSON instance to decode into:
data MySignals = MySignals { count :: Int, label :: Text }
deriving (Generic)
deriving anyclass (FromJSON)
handler :: WAI.Request -> IO ()
handler req = do
Right signals <- readSignals req
putStrLn $ "Count is: " <> show (count signals)
isDatastarRequest :: Request -> Bool Source #
Check whether a request was initiated by Datastar.
Datastar adds a datastar-request header to its SSE requests. Use this to
distinguish Datastar requests from normal page loads — for example, to
serve either an SSE stream or a full HTML page from the same route.
app req respond
| isDatastarRequest req =
respond $ sseResponse $ \gen -> ...
| otherwise =
respond $ responseLBS status200 [] fullPageHtml
renderEvent :: DatastarEvent -> Builder Source #