Your first bri app in 15 minutes
bri is cljgo’s application framework: the batteries of Spring Boot, the manners of a library — you call it, it never calls you. Nothing is scanned, nothing is ambient; every generated file is yours.
-
Generate
Section titled “Generate”$ cljgo new myapp --template web$ cd myapp--template webis not a detail to skim past.cljgo newbelongs to the language, and the language does not assume you are writing a web app: with no--templateit hands youlib, a library. bri is a framework cljgo ships in the box — one template of three (lib,cli,web) — not what cljgo is.You get the blessed layout, all plain files:
src/app/main.cljg the app — THE golden page, T1 editionconf.edn config: one EDN map (+ :profiles)conf.schema.edn optional; enforced because it existspublic/app.css a real stylesheet, served from disk (#NOBUILD)test/app/main_test.cljg one passing test (in-process http client)build.cljgo the build plan.gitignoreThose files are not generated from strings hidden in the compiler: they are copied from a template — a directory of real, runnable source in the cljgo repo (
templates/web/), embedded in the binary, socljgo newneeds no network and can never hand you a version of the app that doesn’t match your toolchain. CI generates that template, runs its test and boots it on every commit; the app you get is the app we test.The template is written with the app name
newapp;cljgo new myapp --template webrenames it — in file contents and in file names — and that is the only substitution there is.Your own template is just a directory shaped the same way:
$ cljgo new myapp --template ../our-house-template -
$ cljgo devbri dev — myappprofile : devnREPL : nrepl://127.0.0.1:57123 (.nrepl-port written)reload : re-(def) a handler var at the REPL — routes hold #'varsbri: listening on http://localhost:3000
Open http://localhost:3000 — a styled page, CSS from
public/, no pipeline.cljgo devalso:- attaches an nREPL (the
.nrepl-portfile is the editor convention) — connect from your editor; - turns on dev warnings: a route holding a plain fn (instead of a
#'var) is called out, because it silently loses liveness; - serves with the default middleware stack — access-log, recover (the error funnel), sessions (signed cookies), JSON negotiation, CSRF. Security is what you didn’t type.
- attaches an nREPL (the
-
Change it live
Section titled “Change it live”Routes hold vars, and vars deref per request. From your connected REPL:
(in-ns 'app.main)(defn home [_req](http/ok (html/page [:h1 "hello from the REPL"])))Refresh the browser. No restart, no reload machinery — the running server sees the new definition because
#'homeis on the routes page. -
Read the page you own
Section titled “Read the page you own”src/app/main.cljgis under a page and is the whole app:(ns app.main(:require [bri.http :as http][bri.html :as html][bri.config :as config]))(def cfg (config/load!)) ; conf.edn + APP_* env. Reads a file, no more.(defn home [_req](http/ok (html/page {:title "myapp"} [:h1 "It's alive."])))(def routes[["GET /{$}" #'home]["GET /static/" (http/dir "public")]["GET /health" (http/health {})]])(defn -main [& _args](http/serve routes {:port (:port cfg)}))The shape is the doctrine: top-level defs construct values, with no I/O (requiring
app.mainis side-effect-free — that’s why tests can load it);-mainstarts the world; routes are data on Go’s own ServeMux patterns; the safe middleware stack applies because you didn’t type one. Config is one plain map — see bri.config. -
$ cljgo testRan 1 tests containing 2 assertions.0 failures, 0 errors.
The generated test uses the in-process client — same mount, same middleware, no socket:
(deftest home-page-renders(let [res (http/request main/routes {:method "GET" :path "/"})](is (= 200 (:status res))))) -
See what you’re running
Section titled “See what you’re running”$ cljgo config ; every key, its value, and the layer that won$ cljgo routes ; every route + the effective middleware stack
Where next
Section titled “Where next”- bri.http — handlers, middleware, the error funnel
- bri.html — pages, forms, escaping
- bri.config — profiles, env, the schema
- New to cljgo itself? Start at the quickstart.
The data layer (bri.db, migrations, the embedded dev Postgres) and jobs/cache land in the next tiers of the app-framework change.
Questions or feedback on this page? Comment below with your GitHub account — comments are public and live in the project's GitHub Discussions.