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"
}
}