Examples

Hello World

Walk through the smallest reference application.

The reference

The 01-hello-world example contains deps.edn, app.edn, bb.edn, and a ClojureScript namespace. Its Babashka tasks compile and run the same server artifact across Node, Deno and Bun using the pinned official ClojureScript Git revision 56a94ac13bfac22108e4d868dfd4763edaceb864. A synchronous handler remains sufficient for this example; use native ^:async and await when a handler needs asynchronous work.

Minimal response

(defn handler [_request]
  {:status 200 :headers {"Content-Type" "text/plain"}
   :body "Hello World"})

Run it

git clone https://github.com/OUTCASTGEEK-TECH/fast-twitch.git
cd fast-twitch
git checkout a4eb73f96836d034c7a24fe09dc7d51e4c581f64
cd examples/01-hello-world
npm install
bb build
bb run-node
# Alternatively: bb run-deno or bb run-bun
# Or let Deno serve the exported Fetch entry: bb deno-serve

Extend it

Add a route, inspect its path parameter, and replace plain text with Hiccup-rendered HTML. Learn the Integrant composition.

The upstream 07-native-transports examples add HTTP forwarding, WebSocket, SSE, bounded stream reading, MessageChannel and TCP exchanges. See how to build and run them.

Send a request and inspect both outputs

curl -i 'http://127.0.0.1:6464/?name=newbie'

Expect HTTP 200, a UTF-8 text content type, Hello from fast-twitch. and a printed request map. The server terminal prints the same request-map information. The example deliberately shows the raw :query-string; it has no parameter middleware, so name=newbie is not yet a parsed :query-params value. The original native Request is omitted only from the human-readable printout, not from the handler's real input.

Follow the complete program below

hello-handler receives the map and builds the response. handler applies ft-handler once with protocol/remote-addr options. run reads PORT/HOSTNAME and starts the adapted handler. shutdown requests forced shutdown and prints after completion. The exported fetch function supports the Fetch-only build used by bb deno-serve.

deps.edn declares compiler/framework dependencies; app.edn chooses the namespace and output; bb.edn coordinates build/run tasks. The browser does not run this server namespace. The compiled server artifact is executed by Node, Bun or Deno.

Change one behavior

Replace the greeting in hello-handler, rebuild and request the same URL. Then add query parsing using the forms example, rather than expecting destructuring alone to decode a query string. If port 6464 is occupied, run PORT=7777 bb run-node and use that port in curl. Stop the previous instance before starting another on the same address.

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/01-hello-world/src/hello/server.cljs

View this exact file on GitHub.

(ns hello.server
  [:require-macros [fast-twitch.macros :refer [env-var]]]
  [:require [cljs.pprint :as pprint]
            [fast-twitch.routing :as routing :refer [start-server! stop-server!]]]
  [:refer-global :only
                 [Number console globalThis]])

(def request-options
  {:protocol "HTTP/1.1" :remote-addr "127.0.0.1"})

(defn greet-one
  [name]
  (str name " is the one"))

(comment
  (greet-one "Lb")
  ;;
)

(defn pretty
  "Turn a ClojureScript value into a nicely spaced string."
  [value]
  (with-out-str (pprint/pprint value)))

(defn request-for-humans
  "The real request map includes the original Fetch Request under a namespaced
  key. That object is useful to advanced users, but noisy in hello world output."
  [request]
  (dissoc request ::routing/request))

(defn hello-handler
  "A Ring-style handler: request map in, response map out."
  [request]
  (let [request-map (request-for-humans request)
        body (str "Hello from fast-twitch.\n\n"
                  "The handler received this request map:\n\n"
                  (pretty request-map))]
    (console/log "Request map received by hello-handler:")
    (console/log (pretty request-map))
    {:status 200 :headers {"Content-Type" "text/plain; charset=utf-8"} :body body}))

(def handler
  (routing/ft-handler hello-handler request-options))

(defn shutdown
  []
  (stop-server! :force true
                :callback (fn []
                            (console.log "Server stopped"))))

(defn run
  []
  (start-server! handler
                 {:port (Number (or (env-var "PORT") 6464))
                  :hostname (or (env-var "HOSTNAME") "127.0.0.1")
                  :onListen (fn [addr]
                              (console/log (str "Hello world server listening at http://"
                                                  (aget addr "hostname")
                                                ":" (aget addr "port"))))}))

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

(defn ^:export fetch
  [request]
  (handler request))

Source: examples/01-hello-world/deps.edn

View this exact file on GitHub.

{:aliases {:nREPL {:extra-deps {cider/cider-nrepl {:mvn/version "0.62.2"}
                                cider/piggieback {:mvn/version "0.7.0"}
                                nrepl/nrepl {:mvn/version "1.7.0"}}
                   :main-opts
                     ["-m" "nrepl.cmdline" "--middleware"
                      "[cider.nrepl/cider-middleware, cider.piggieback/wrap-cljs-repl]"]}
           :test {:extra-paths ["test"]}}
 :deps {fast-twitch/fast-twitch {:local/root "../.."}
        org.clojure/clojurescript {:git/sha "56a94ac13bfac22108e4d868dfd4763edaceb864"
                                   :git/url "https://github.com/clojure/clojurescript"}}
 :paths ["src"]}

;; Start the example nREPL server
;; clj -M:nREPL

Source: examples/01-hello-world/app.edn

View this exact file on GitHub.

{:aot-cache true
 :cache-analysis true
 :dump-core true
 :fn-invoke-direct true
 :infer-externs :all
 :language-in :es-next
 :language-out :es-next
 :main hello.server
 :optimizations :simple
 :optimize-constants true
 :output-dir "build"
 :output-to "server.cjs"
 :output-wrapper "%s\nexports.fetch = hello.server.fetch;\n"
 :parallel-build true
 :pretty-print true
 :source-map "server.cjs.map"
 :stable-names true
 :static-fns true
 :target :nodejs}

Source: examples/01-hello-world/bb.edn

View this exact file on GitHub.

{:tasks {build {:doc "Compile ClojureScript to server.cjs"
                :task (shell "clj" "-M" "-m" "cljs.main" "-co" "app.edn" "-c")}
         deno-serve {:doc "Build and serve the exported Fetch handler with Deno"
                     :task (let [port (or (System/getenv "PORT") "6464")
                                 jobs (or (System/getenv "DENO_JOBS") "1")]
                             (shell "clj"
                                    "-M"
                                    "-m"
                                    "cljs.main"
                                    "-co"
                                    "app.edn"
                                    "-co"
                                    (pr-str {:nodejs-rt false
                                             :output-dir "build/serve"
                                             :output-to "build/server-serve.cjs"
                                             :source-map "build/server-serve.cjs.map"})
                                    "-c")
                             (shell {:extra-env {"DENO_JOBS" jobs "PORT" port}}
                                    "deno"
                                    "serve"
                                    "--parallel"
                                    "-A"
                                    "--port"
                                    port
                                    "build/server-serve.cjs"))}
         run-bun {:depends [build]
                  :doc "Build and run server.cjs with Bun"
                  :task (shell "bun server.cjs")}
         run-deno {:depends [build]
                   :doc "Build and run server.cjs with Deno"
                   :task (shell "deno run -A server.cjs")}
         run-node {:depends [build]
                   :doc "Build and run server.cjs with Node"
                   :task (shell "node server.cjs")}}}

Source: examples/01-hello-world/package.json

View this exact file on GitHub.

{
  "name": "fast-twitch-01-hello-world",
  "private": true,
  "type": "commonjs",
  "scripts": {
    "build": "bb build",
    "node": "bb run-node",
    "deno": "bb run-deno",
    "bun": "bb run-bun"
  },
  "devDependencies": {
    "@cljs-oss/module-deps": "^1.1.1"
  }
}

Source: examples/01-hello-world/deno.jsonc

View this exact file on GitHub.

{
  "tasks": {
    "build": "bb build",
    "start": "bb run-deno",
    "serve": "bb deno-serve"
  }
}

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.