Getting started
Compile and run your first Fast-Twitch server.
Prerequisites
Install Node.js, Java 21 or newer with the Clojure CLI, and Babashka. The upstream examples pin the official ClojureScript Git revision 56a94ac13bfac22108e4d868dfd4763edaceb864, supporting native ^:async functions, await, checked destructuring and :refer-global. Their deps.edn supplies the compiler and Fast-Twitch; the framework also brings its Malli dependency for native option validation. Install the example's npm dependencies before compiling.
Run the reference example
Clone the public repository and run the documented revision:
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
bb build runs clj -M -m cljs.main -co app.edn -c, producing server.cjs. The example's default port is 6464. bb run-node, bb run-deno and bb run-bun each build and run this artifact. bb deno-serve builds a separate artifact without CLI startup and lets Deno serve its exported Fetch handler. Run the example separately from another application to avoid a port conflict.
Check the result before changing it
In another terminal:
curl -i 'http://127.0.0.1:6464/?name=Ada'
Expect status 200, a text content type, the greeting and a printed request map. Look at the server terminal too. A successful compiler exit only means the artifact built; the curl response confirms the listener and handler run. PORT=7777 bb run-node selects a different port if 6464 is already occupied.
The Hello World walkthrough includes the complete namespace, dependency file, compiler options and run tasks on the page. You do not need to reconstruct a working project from the excerpts below.
Create a handler
The following namespace aliases are used in these excerpts:
(ns application.hello
(:require [fast-twitch.routing :as routing]))
A handler accepts a request map and returns a response map. Set the content type explicitly.
(defn home [_]
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body "<h1>Hello</h1>"})
Adapt and start
(def handler
(routing/ft-handler home
{:protocol "HTTP/1.1" :remote-addr "127.0.0.1"}))
(routing/start-server! handler
{:hostname "127.0.0.1" :port 6464})
Stop the server
(routing/stop-server! :force true
:callback #(println "Stopped"))
stop-server! returns a Promise when a server is running. The callback runs after shutdown completes; an async lifecycle function can await that Promise before releasing other resources.
Configured server API
The existing routing and lifecycle functions remain available. The new server/listen! accepts an ordinary Ring handler directly and exposes codec and transport configuration:
(ns application.server
(:require [fast-twitch.server.core :as server]))
(def http-server
(server/listen! (fn [_] (server/response "Hello, Fast-Twitch!"))
{:transport :http :hostname "127.0.0.1" :port 6464}))
;; When finished, await this result from an async lifecycle function.
(server/close! http-server {:force true})
Choose one startup path for a listener. routing/start-server! takes the Fetch handler created above; server/listen! performs that adaptation for a Ring handler itself. Learn about configured clients and native transports.
Continue with request and response maps.
Build files and runtime choices
deps.edn chooses the framework/compiler revision and Clojure dependencies. app.edn chooses the ClojureScript entry namespace, JavaScript target and output path. bb.edn invokes the compiler and then the selected runtime. package.json declares JavaScript dependencies needed by imported namespaces. Keep all four aligned rather than copying only a response function.
Node, Bun and Deno run server-side JavaScript; the browser is a separate client. bb deno-serve differs from bb run-deno: it compiles an exported Fetch handler without executing the CLI startup function, then asks Deno to serve that handler. Worker examples similarly export Fetch and let their runtime own listening.
Choose what to learn next
| You want to build | Continue with |
|---|---|
| A route and parameterized handler | Routing, request destructuring example |
| A rendered HTML site | Hiccup & Integrant guide, complete project |
| Forms, cookies and sessions | Middleware guide, todo/session project |
| Uploads and static files | File guide, upload project |
| A JSON HTTP client/server | Native option tables, HTTP forwarding project |
| WebSocket, SSE, TCP or ports | Native transport guide, example index |
| A Fetch Worker | Celld project, workerd project |
Common startup failures
A compiler error about async/await means the compiler/configuration may differ from the pinned example. A missing JavaScript package is fixed by installing that example's declared dependencies, not by rewriting framework imports. Address-in-use means another process owns the selected port. Runtime capability errors mean the selected transport needs an API the current runtime does not expose; consult the capability table.