| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Hypermedia.Datastar
Description
Datastar is a hypermedia framework: instead of building a JSON API and a JavaScript SPA, you write HTML on the server and let Datastar handle the interactivity. The browser sends requests, the server holds the connection open as a server-sent event (SSE) stream, and pushes HTML fragments, signal updates, or scripts back to the browser as things change.
This SDK provides the server-side Haskell API. It builds on WAI so it works with Warp, Scotty, Servant, Yesod, or any other WAI-compatible framework.
Minimal example
import Hypermedia.Datastar
import Network.Wai (Application, responseLBS, requestMethod, pathInfo)
import Network.Wai.Handler.Warp qualified as Warp
import Network.HTTP.Types (status404)
app :: Application
app req respond =
case (requestMethod req, pathInfo req) of
("GET", ["hello"]) ->
respond $ sseResponse $ \gen ->
sendPatchElements gen (patchElements "<div id=\"msg\">Hello!</div>")
_ ->
respond $ responseLBS status404 [] "Not found"
main :: IO ()
main = Warp.run 3000 app
Module guide
- Hypermedia.Datastar.PatchElements — send HTML to morph into the DOM
- Hypermedia.Datastar.PatchSignals — update the browser's reactive signals
- Hypermedia.Datastar.ExecuteScript — run JavaScript in the browser
- Hypermedia.Datastar.WAI — SSE streaming, signal decoding, request helpers
- Hypermedia.Datastar.Types — protocol types and defaults
Further reading
- Datastar homepage — guides, reference, and examples
- Clojure SDK docs — excellent Datastar walkthrough that applies across SDKs
Synopsis
- data EventType
- data ElementPatchMode
- data ElementNamespace
- data PatchElements = PatchElements {}
- patchElements :: Text -> PatchElements
- removeElements :: Text -> PatchElements
- data PatchSignals = PatchSignals {
- psSignals :: Text
- psOnlyIfMissing :: Bool
- psEventId :: Maybe Text
- psRetryDuration :: Int
- patchSignals :: Text -> PatchSignals
- data ExecuteScript = ExecuteScript {
- esScript :: Text
- esAutoRemove :: Bool
- esAttributes :: [Text]
- esEventId :: Maybe Text
- esRetryDuration :: Int
- executeScript :: Text -> ExecuteScript
- data ServerSentEventGenerator
- sseResponse :: DatastarLogger -> (ServerSentEventGenerator -> IO ()) -> Response
- sendPatchElements :: ServerSentEventGenerator -> PatchElements -> IO ()
- sendPatchSignals :: ServerSentEventGenerator -> PatchSignals -> IO ()
- sendExecuteScript :: ServerSentEventGenerator -> ExecuteScript -> IO ()
- readSignals :: FromJSON a => Request -> IO (Either String a)
- isDatastarRequest :: Request -> Bool
- data DatastarLogger
- nullLogger :: DatastarLogger
- stderrLogger :: DatastarLogger
Types
The two SSE event types defined by the Datastar protocol.
Every event the server sends is one of these. EventPatchElements covers
both DOM patching and script execution (the protocol encodes executeScript
as a special case of element patching). EventPatchSignals updates the
browser's reactive signal store.
Constructors
| EventPatchElements | Sent as |
| EventPatchSignals | Sent as |
data ElementPatchMode Source #
How the patched HTML should be applied to the DOM.
The default mode is Outer, which replaces the target element (matched by
its id attribute) including the element itself. This works well with
Datastar's morphing algorithm, which preserves focus, scroll position, and
CSS transitions during the replacement.
Constructors
| Outer | Replace the target element and its contents (the default). |
| Inner | Replace only the target element's children, keeping the element itself. |
| Remove | Remove the target element from the DOM entirely. |
| Replace | Replace the target element without morphing (a hard swap). |
| Prepend | Insert the new content as the first child of the target element. |
| Append | Insert the new content as the last child of the target element. |
| Before | Insert the new content immediately before the target element. |
| After | Insert the new content immediately after the target element. |
Instances
| Show ElementPatchMode Source # | |
Defined in Hypermedia.Datastar.Types Methods showsPrec :: Int -> ElementPatchMode -> ShowS # show :: ElementPatchMode -> String # showList :: [ElementPatchMode] -> ShowS # | |
| Eq ElementPatchMode Source # | |
Defined in Hypermedia.Datastar.Types Methods (==) :: ElementPatchMode -> ElementPatchMode -> Bool # (/=) :: ElementPatchMode -> ElementPatchMode -> Bool # | |
data ElementNamespace Source #
The XML namespace for the patched elements.
Almost all content uses HtmlNs (the default). Use SvgNs or MathmlNs
when patching inline SVG or MathML elements so that Datastar creates them
in the correct namespace.
Constructors
| HtmlNs | Standard HTML namespace (the default). |
| SvgNs | SVG namespace — use when patching |
| MathmlNs | MathML namespace — use when patching |
Instances
| Show ElementNamespace Source # | |
Defined in Hypermedia.Datastar.Types Methods showsPrec :: Int -> ElementNamespace -> ShowS # show :: ElementNamespace -> String # showList :: [ElementNamespace] -> ShowS # | |
| Eq ElementNamespace Source # | |
Defined in Hypermedia.Datastar.Types Methods (==) :: ElementNamespace -> ElementNamespace -> Bool # (/=) :: ElementNamespace -> ElementNamespace -> Bool # | |
Patch Elements
data PatchElements Source #
Configuration for a datastar-patch-elements SSE event.
Construct values with patchElements or removeElements, then customise
with record updates.
Constructors
| PatchElements | |
Fields
| |
Instances
| Show PatchElements Source # | |
Defined in Hypermedia.Datastar.PatchElements Methods showsPrec :: Int -> PatchElements -> ShowS # show :: PatchElements -> String # showList :: [PatchElements] -> ShowS # | |
| Eq PatchElements Source # | |
Defined in Hypermedia.Datastar.PatchElements Methods (==) :: PatchElements -> PatchElements -> Bool # (/=) :: PatchElements -> PatchElements -> Bool # | |
patchElements :: Text -> PatchElements Source #
Build a PatchElements event with sensible defaults.
The HTML is sent as-is and Datastar matches target elements by their id
attribute.
patchElements "<div id=\"greeting\">Hello!</div>"
removeElements :: Text -> PatchElements Source #
Remove elements from the DOM matching a CSS selector.
removeElements "#notification" removeElements ".stale-row"
Patch Signals
data PatchSignals Source #
Configuration for a datastar-patch-signals SSE event.
Construct values with patchSignals, then customise with record updates.
Constructors
| PatchSignals | |
Fields
| |
Instances
| Show PatchSignals Source # | |
Defined in Hypermedia.Datastar.PatchSignals Methods showsPrec :: Int -> PatchSignals -> ShowS # show :: PatchSignals -> String # showList :: [PatchSignals] -> ShowS # | |
| Eq PatchSignals Source # | |
Defined in Hypermedia.Datastar.PatchSignals | |
patchSignals :: Text -> PatchSignals Source #
Build a PatchSignals event with sensible defaults.
The argument is a JSON object (as Text) describing the signals to update.
patchSignals "{\"count\": 42, \"label\": \"hello\"}"
Execute Script
data ExecuteScript Source #
Configuration for executing a script in the browser.
Construct values with executeScript, then customise with record updates.
Constructors
| ExecuteScript | |
Fields
| |
Instances
| Show ExecuteScript Source # | |
Defined in Hypermedia.Datastar.ExecuteScript Methods showsPrec :: Int -> ExecuteScript -> ShowS # show :: ExecuteScript -> String # showList :: [ExecuteScript] -> ShowS # | |
| Eq ExecuteScript Source # | |
Defined in Hypermedia.Datastar.ExecuteScript Methods (==) :: ExecuteScript -> ExecuteScript -> Bool # (/=) :: ExecuteScript -> ExecuteScript -> Bool # | |
executeScript :: Text -> ExecuteScript Source #
Build an ExecuteScript event with sensible defaults.
The argument is the JavaScript source code to run in the browser.
executeScript "document.getElementById(\"name\").focus()"
WAI
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.
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
Logger
data DatastarLogger Source #