datastar/sdk/clojure
JeremS 65a301da9e
Fixing issue #571 / minor changes, improvements (#590)
* Refactor: Ring jetty adapter is now a generic ring adapter

Minor changes:

- Properly eliding SSE retry duration
- Added a small utility in the main api.
- Added a new option to the ->sse-response functions to support
  modifying the HTTP response status.
- Fixed typos and errors in the docs.
- using consts version number.
- Prepared tests for rj9a
- Properly killing webrivers when shutting down tests

* Docs: fixing the fixed typo
2025-02-04 08:15:06 -06:00
..
.clj-kondo Clojure SDK (#540) 2025-01-29 16:28:08 -06:00
adapter-http-kit Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
adapter-ring Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
doc Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
malli-schemas Clojure SDK (#540) 2025-01-29 16:28:08 -06:00
sdk Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
sdk-tests Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
src Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
test-resources Clojure SDK (#540) 2025-01-29 16:28:08 -06:00
.gitignore Clojure SDK (#540) 2025-01-29 16:28:08 -06:00
CHANGELOG.md Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
README.md Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
bb.edn Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00
deps.edn Fixing issue #571 / minor changes, improvements (#590) 2025-02-04 08:15:06 -06:00

README.md

Datastar Clojure SDK

We provide several libraries for working with Datastar:

  • A generic SDK to generate and send Datastar event using a SSE generator abstraction defined by the starfederation.datastar.clojure.protocols/SSEGenerator protocol. This gives us a common API working for each implementation of the protocol.
  • Libraries containing implementations of the SSEGenerator protocol that work with specific ring adapters.
  • A library containing malli schemas for the SDK.

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/tree/develop"
              :git/sha "LATEST_SHA"
              :deps/root "sdk/clojure/sdk"}
  • ring implementation
datastar/ring {:git/url "https://github.com/starfederation/datastar/tree/develop"
               :git/sha "LATEST_SHA"
               :deps/root "sdk/clojure/adapter-ring"}}

ring-compliant/adapter "Coordinate for the ring compliant adater you wanna use."

[!important] This is library that should work for ring compliant adapters, specifically those that use the ring.core.protocols/StreamableResponseBody protocol for their response bodies.

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

Usage

Concepts

By convention adapters provide a single ->sse-response function. This function returns a valid ring response tailored to work with the used ring adapter. It takes callbacks that receive an implementation of the SSE generator as the first argument.

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

Short example

Start by requiring the 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 for your handlers:

(defn sse-handler [request]
  (hk-gen/->sse-response request
    {: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, store it somewhere and use it later:

(def !connections (atom #{}))


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


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

[!important] Check doctrings / Readmes for the specific adapter you use.

[!note] Check the docstrings in the starfederation.datastar.clojure.api namespace for more details on the SDK functions.

Adapter differences:

Ring adapters are not made equals. Here are some the differences for the ones we provide:

Adapter return values from the SDK event sending functions
ring irrelevant
http-kit boolean, from org.http-kit.server/send!

[!note] The SDK's event sending functions return whatever the adapter's implementation of starfederation.datastar.clojure.protocols/send-event! returns.

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

[!note] You may keep the connection open in ring-jetty sync mode by somehow blocking the thread handling the request.

Adapter connection lifetime in ring async mode
ring alive until the client or the server explicitely closes the connection
http-kit alive until the client or the server explicitely closes the connection
Adapter sending an event on closed connection
ring exception thrown
http-kit fn returns false, from org.http-kit.server/send!

[!note] This is one way to detect the connection has been closed by the client.

[!important] At the moment, the ring-jetty adapter needs to send 2 small messages or 1 big message to detect a closed connection. There must be some buffering happening independent of our implementation.

Adapter :on-open callback parameters
ring-jetty [sse-gen]
http-kit [sse-gen]
Adapter :on-close callback parameters
ring-jetty [sse-gen]
http-kit [sse-gen status-code]

TODO:

  • Streamlined release process (cutting releases and publish jar to a maven repo)
  • Consider uniformizing the adapters behavior on connection closing (throwing in all adapters?)
  • Review the etoin tests, there are some race conditions
  • More official examples