Examples

HTTP forwarding & codecs

Forward a native response, edit its status and headers, then decode its body as text.

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 http
node target/http.cjs

What happens

The program creates /origin and /forward routes and starts a listener on port 0. A readiness Promise receives the actual address from :on-listen, so there is no arbitrary sleep or hardcoded unused port. /forward requests /origin with the default native codec, then changes the returned map's status to 201 and adds x-forwarded: yes without collecting its body.

The outer request uses :codec :text, collecting the final body. The assertions require status 201, the forwarding header and "origin body". Successful completion prints HTTP forwarding and map edits passed and exits after server/close!.

Read the boundaries

routing/routes still uses ordinary route maps. server/listen! accepts that Ring handler directly. client/request! returns a response map carrying the native body; editing the map does not call .text on that stream. Only the outer text-codec request consumes it. The base and readiness atoms belong to this example invocation, not to all HTTP requests in an application.

Exercise

Add a response header in /origin and verify that /forward preserves it. Change the outer codec to :bytes and inspect the Uint8Array. Set a decoded body limit below the origin body's byte size and inspect the rejection; do not attempt to reuse the consumed response after that failure.

HTTP options and codecs · Client API.

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/http.cljs

View this exact file on GitHub.

(ns native-demo.http
  (: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]))

(defn ^:async -main
  []
  (let [base (atom nil)
        listened (atom nil)
        ready (Promise. (fn [resolve _]
                          (reset! listened resolve)))
        handler (routing/routes [{:pattern "/origin"
                                  :handler (fn [_]
                                             (routing/response "origin body"))}
                                 {:pattern "/forward"
                                  :handler
                                    (^:async fn
                                     [_]
                                     (let [response (await (client/request!
                                                             {:url (str @base "/origin")
                                                              :request-method :get
                                                              :headers {}}))]
                                       (-> response
                                           (assoc :status 201)
                                           (assoc-in [:headers "x-forwarded"] "yes"))))}]
                                (fn [_]
                                  (routing/not-found "missing")))
        serving (server/listen! handler {:port 0 :on-listen @listened})]
    (try (reset! base (str "http://127.0.0.1:" (.-port (await ready))))
         (let [response (await (client/request! {:url (str @base "/forward")
                                                 :request-method :get
                                                 :headers {}}
                                                {:codec :text}))]
           (assert (= 201 (:status response)))
           (assert (= "yes" (get-in response [:headers "x-forwarded"])))
           (assert (= "origin body" (:body response)))
           (.log console "HTTP forwarding and map edits passed"))
         (finally (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.