datastar/sdk/clojure
Jérémy 8b84986410
Fix: added proper clj-kondo config fixing unresolved vars warnings (#817)
Fix: handling of the HTTP keep-alive header
Refactor: simpler concurrency handling in one test.
2025-04-07 10:50:42 -06:00
..
.clj-kondo Fix: added proper clj-kondo config fixing unresolved vars warnings (#817) 2025-04-07 10:50:42 -06:00
adapter-http-kit Change: removed deprecated api. (#810) 2025-03-31 10:41:32 -06:00
adapter-ring Fix: added proper clj-kondo config fixing unresolved vars warnings (#817) 2025-04-07 10:50:42 -06:00
doc Clojure SDK - Ability to gzip sse streams - improvements - new how to code snippet (#747) 2025-03-13 09:41:49 -06:00
malli-schemas Remove settling from SSE events and SDKs (#764) 2025-03-15 16:25:33 -06:00
sdk Fix: added proper clj-kondo config fixing unresolved vars warnings (#817) 2025-04-07 10:50:42 -06:00
sdk-tests Remove settling from SSE events and SDKs (#764) 2025-03-15 16:25:33 -06:00
src Fix: added proper clj-kondo config fixing unresolved vars warnings (#817) 2025-04-07 10:50:42 -06:00
test-resources Clojure SDK (#540) 2025-01-29 16:28:08 -06:00
.gitignore Clojure sdk: Fixes, clojure snippets (#672) 2025-02-17 12:34:58 -06:00
CHANGELOG.md Fix: added proper clj-kondo config fixing unresolved vars warnings (#817) 2025-04-07 10:50:42 -06:00
README.md Clojure SDK - Ability to gzip sse streams - improvements - new how to code snippet (#747) 2025-03-13 09:41:49 -06:00
bb.edn Clojure SDK - Ability to gzip sse streams - improvements - new how to code snippet (#747) 2025-03-13 09:41:49 -06:00
deps.edn Fix: added proper clj-kondo config fixing unresolved vars warnings (#817) 2025-04-07 10:50:42 -06:00

README.md

Datastar Clojure SDK

We provide several libraries for working with Datastar:

  • A generic SDK to generate and send SSE events formatted for Datastar.
  • A SSE generator abstraction defined by the starfederation.datastar.clojure.protocols/SSEGenerator protocol as well as several libraries implementing it to work with specific ring adapters.
  • A library containing malli schemas covering the generic API and our adapter implementations.

There currently are adapter implementations for:

If you want to roll your own adapter implementation, see implementing-adapters.

Installation

For now the libraries are distributed as git dependencies. You need to add a dependency for each library you use.

[!important] This project is new and there isn't a release process yet other than using git shas. Replace LATEST_SHA in the git coordinates below by the actual latest commit sha of this repository.

To your deps.edn file you can add the following coordinates:

  • SDK
{datastar/sdk {:git/url "https://github.com/starfederation/datastar/"
               :git/sha "LATEST_SHA"
               :deps/root "sdk/clojure/sdk"}}
  • ring implementation
{datastar/ring {:git/url "https://github.com/starfederation/datastar/"
                :git/sha "LATEST_SHA"
                :deps/root "sdk/clojure/adapter-ring"}

 ring-compliant/adapter "Coordinate for the ring compliant adater you want to use."}

By ring compliant adapter we mean adapters that are implementing the ring.core.protocols/StreamableResponseBody protocol to deal with response bodies.

  • http-kit implementation
{datastar/http-kit {:git/url "https://github.com/starfederation/datastar/"
                    :git/sha "LATEST_SHA"
                    :deps/root "sdk/clojure/adapter-http-kit"}}
  • Malli schemas:
{datastar/malli-schemas {:git/url "https://github.com/starfederation/datastar/"
                         :git/sha "LATEST_SHA"
                         :deps/root "sdk/clojure/malli-schemas"}}

Usage

Basic Concepts

By convention SDK adapters provide a single ->sse-response function. This function returns a valid ring response tailored to work with the ring adapter it is made for. This function will receive an implementation of SSEGenerator protocol also tailored to the ring adapter used.

You then use the Datastar SDK functions with the SSE generator.

Short example

Start by requiring the main API and an adapter. With Http-kit for instance:

(require '[starfederation.datastar.clojure.api :as d*])
         '[starfederation.datastar.clojure.adapter.http-kit :as hk-gen])

Using the adapter you create ring responses in your handlers:

(defn sse-handler [request]
  (hk-gen/->sse-response request
    {hk-gen/on-open
     (fn [sse-gen]
       (d*/merge-fragment! sse-gen "<div>test</div>")
       (d*/close-sse! sse-gen))}))

In the callback we use the SSE generator sse-gen with the Datastar SDK functions.

Depending on the adapter you use, you can keep the SSE generator open by storing it somewhere and use it later:

(def !connections (atom #{}))


(defn sse-handler [request]
  (hk-gen/->sse-response request
    {hk-gen/on-open
     (fn [sse-gen]
       (swap! !connections conj sse-gen))

     hk-gen/on-close
     (fn [sse-gen status]
       (swap! !connections disj sse-gen))}))


(defn broadcast-fragment! [fragment]
  (doseq [c @!connections]
    (d*/merge-fragment! c fragment)))

Check the docstrings in the starfederation.datastar.clojure.api namespace for more details.

Advanced features

This SDK is essentially a tool to manage SSE connections with helpers to format events the way the Datastar framework expects them on the front end.

It provides advanced functionality for managing several aspects of SSE.

You can find more information in several places:

  • the docstings for the ->sse-response function you are using.
  • the SSE design notes document details what considerations are taken into account in the SDK.
  • the write profiles document details the tools the SDK provides to control the buffering behaviors of a SSE stream and how to use compression.
  • the adapter implementation guide lists the conventions by which SDK adapters are implemented if the need to implement your own ever arises.

Adapter behaviors:

Ring adapters are not made equals. This leads to our SSE generators not having the exact same behaviors in some cases.

SSE connection lifetime in ring when trying to store the sse-gen somewhere

With ring sync

Adapter
ring same as the thread creating the sse response
http-kit alive until the client or the server explicitely closes the connection

You may keep the connection open in ring sync mode by somehow blocking the thread handling the request. This is a valid strategy when using java's virtual threads.

With ring async

Adapter
all adapters alive until the client or the server explicitely closes the connection

Detecting a closed connection

Adapter
ring Sending events on a closed connection will fail at some point and the sse-gen will close itself
http-kit Http-kit detects closed connections by itself and closes the sse-gen

At this moment, when using the ring adapter and jetty, our SSE generators need to send 2 small events or 1 big event to detect a closed connection. There must be some buffering happening independent of our implementation.

TODO:

  • Streamlined release process (cutting releases and publish jar to a maven repo)
  • Review the etaoin tests, there are some race conditions

License

License