datastar-hs
Safe HaskellSafe-Inferred
LanguageHaskell2010

Hypermedia.Datastar.WAI

Description

This module connects Datastar to WAI (Web Application Interface), Haskell's standard HTTP server interface. It provides:

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 datastar query parameter
  • POST requests: signals are in the request body as JSON

Use readSignals with any FromJSON instance to decode them.

Synopsis

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 

Fields

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>")

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