Examples

WebSocket echo

Upgrade an ordinary route, connect with a native client and echo one message.

Build and run

Requires Java 21+, Clojure CLI and the runtime named below. The example pins the compiler that supports native ^:async and await. Run these commands in a separate terminal from other examples.

git clone https://github.com/OUTCASTGEEK-TECH/fast-twitch.git
cd fast-twitch
git checkout a4eb73f96836d034c7a24fe09dc7d51e4c581f64
cd examples/07-native-transports
clojure -M:build websocket
bun target/websocket.cjs
# Alternatively: deno run -A target/websocket.cjs

Expected result

Use Bun or Deno. Successful completion prints Native WebSocket through existing routes and serve passed. On Node the example prints its runtime-support message and does not create an echo server: Node's adapter does not implement a native server upgrade.

Follow the message

  1. /socket returns :ring.websocket/listener with an :on-message function.
  2. server/listen! creates the HTTP listener; :on-listen resolves its actual port.
  3. client/connect! uses :transport :websocket and resolves when the socket opens.
  4. client/send! sends "hello". The server callback receives [connection value] and sends that value back.
  5. The client callback resolves the reply Promise. The assertion compares it with "hello".
  6. finally closes the client connection and awaits HTTP listener shutdown.

The connection-attempt abort signal does not provide a session deadline after open. Give long-lived connections their own lifecycle. Messages are event-driven; receive! is a TCP collector, not a WebSocket receive operation.

Exercises

Return a different string from the server's message callback. Add :on-close with [connection code reason] to observe shutdown. Use client/close! with a valid close code and short reason. Keep the codec native: JSON values need explicit application serialization for WebSocket messages.

WebSocket options and callbacks · WebSocket routes.

Complete source and build files

The files below are copied without abbreviation from Fast-Twitch revision a4eb73f96836d034c7a24fe09dc7d51e4c581f64. The walkthrough and API reference use the same revision.

Source: examples/07-native-transports/src/native_demo/websocket.cljs

View this exact file on GitHub.

(ns native-demo.websocket
  (:require [cljs.core :refer [await]]
            [fast-twitch.routing :as routing]
            [fast-twitch.client.core :as client]
            [fast-twitch.server.core :as server])
  (:refer-global :only [Promise console globalThis]))

(defn ^:async -main
  []
  (if-not (or (aget globalThis "Deno") (aget globalThis "Bun"))
    (.log console "Native server upgrade is available in the Deno and Bun profiles")
    (let [listened (atom nil)
          ready (Promise. (fn [resolve _]
                            (reset! listened resolve)))
          handler (routing/routes [{:pattern "/socket"
                                    :handler (fn [_]
                                               {:ring.websocket/listener
                                                  {:on-message (fn [connection value]
                                                                 (client/send!
                                                                   connection
                                                                   value))}})}]
                                  (fn [_]
                                    (routing/not-found "missing")))
          serving (server/listen! handler {:port 0 :on-listen @listened})
          connection (atom nil)]
      (try (let [received (atom nil)
                 reply (Promise. (fn [resolve _]
                                   (reset! received resolve)))
                 url (str "ws://127.0.0.1:" (.-port (await ready)) "/socket")]
             (reset! connection (await (client/connect!
                                         {:url url}
                                         {:transport :websocket
                                          :listener {:on-message (fn [_ value]
                                                                   (@received value))}})))
             (client/send! @connection "hello")
             (assert (= "hello" (await reply)))
             (.log console "Native WebSocket through existing routes and serve passed"))
           (finally (when @connection (client/close! @connection))
                    (await (server/close! serving {:force true})))))))

(set! *main-cli-fn* -main)

Source: examples/07-native-transports/deps.edn

View this exact file on GitHub.

{:aliases {:build {:main-opts ["-m" "native-demo.build"]}}
 :deps {fast-twitch/fast-twitch {:local/root "../.."}
        org.clojure/clojurescript {:git/sha "56a94ac13bfac22108e4d868dfd4763edaceb864"
                                   :git/url "https://github.com/clojure/clojurescript"}}
 :paths ["src" "dev"]}

Source: examples/07-native-transports/dev/native_demo/build.clj

View this exact file on GitHub.

(ns native-demo.build
  (:require [cljs.build.api :as cljs]))

(def modes
  #{"http" "websocket" "sse" "streams" "ports" "tcp-node" "tcp-deno" "tcp-bun"})

(defn -main
  [mode]
  (doseq [mode (if (= mode "all") (sort modes) [mode])]
    (when-not (modes mode) (throw (ex-info "Choose an example" {:modes modes})))
    (cljs/build "src"
                {:main (symbol (str "native-demo." mode))
                 :target :nodejs
                 :output-to (str "target/" mode ".cjs")
                 :output-dir (str "target/" mode)
                 :optimizations :simple})
    (println mode "COMPILED")))
Source revision a4eb73f96836d034c7a24fe09dc7d51e4c581f64

Built with Fast-Twitch · Integrant · Datastar

Search documentation

Search pages and API symbols

Start hereWelcome to Fast-TwitchRing HTTP applications and configured native clients and transports in ClojureScript.Start hereGetting startedCompile and run your first Fast-Twitch server.Core conceptsRequest & responseUnderstand the maps at the center of a Fast-Twitch application.Core conceptsRoutingMatch URL patterns and compose handlers.Core conceptsMiddlewareWrap handlers with focused request and response behavior.GuidesHTML & IntegrantCompose a UI system with Integrant and render Hiccup.