joetjen.net
EN DE
Web framework

WebGlue

A glue framework, not a monolith — Ring’s simplicity, Phoenix’s ambition. WebGlue is a web framework written in Praxis, made of small, swappable libraries that share one contract: a request is a map, a response is a map, and a handler is a function from one to the other.

In development · v0.8

The idea

Clojure’s Ring is a specification rather than a library: a handler is a plain function, and middleware is a function that wraps a handler. WebGlue takes that pattern and carries it through a whole framework — routing, HTTP servers, templates, sessions, forms, WebSockets, pub/sub, presence and server push — with the completeness of Phoenix or Laravel as the goal.

handler    :: Request -> Response
middleware :: Handler -> Handler

A WebGlue application is a choice of libraries glued together. The framework is the convention, not the code.

Principles

  • Small core, replaceable edges.
  • Standards first: HTTP/1.1, HTTP/2, RFC 6455 WebSockets, IPv4 and IPv6.
  • Design before code: each library starts as a specification.
  • No hidden magic in the hot path. Composition is readable function calls — no macros, no use.
  • The frontend is not a separate ecosystem: it will be Praxis too, compiled for the browser.
  • Complete at release, monolithic never.
  • Glue to the established standard-bearer instead of reinventing it — Bandit, Ecto, Absinthe, Oban, Swoosh, Redix.

Three more decisions are fixed: markup is data — a tree of plain Praxis vectors and maps, never strings, and a template compiles to such a tree too; the wire format is DXN, with JSON as the fallback; configuration is CASC.

A first look

The demo application is one function. Read it from the bottom: the order of the wrapping is the order of the nesting, and this is the only place it is stated.

webglue_demo/apps/web/src/webglue/demo/web.prax
(defn app
  "The application: one function, request to response."
  [] :spec [-> function]
  (-> (router/handler (routes))
      (db/wrap {:repo webglue.demo.store.memory})
      (wrap-user)
      (static/wrap {:root (assets) :at "/assets"})
      (session/wrap {:store (memory/store)})))

Built with Lictor

Since October 2026 WebGlue is no longer planned on its own. Lictor — moderation for live broadcasters, written in Praxis in the same repository — is a WebGlue application, and it is built journey by journey: the site served, then registration, then login. When a journey needs something WebGlue does not have, it is built in WebGlue, on its own branch, during that journey. What a journey leaves out goes into WebGlue’s backlog.

Lictor’s registration brought form bodies, CSRF protection and a Tailwind build into WebGlue 0.8. Its application function has the same shape as the demo’s:

lictor/apps/lictor_elysium/src/lictor/elysium.prax
(let [routed (-> (router/handler (routes))
                 (csrf/wrap)
                 (params/wrap)
                 (session/wrap {:store (sessions/store)})
                 (in-world world))]
  (static/wrap routed {:root (static-root) :at "/assets"}))

The companion demo app, a ticket system, still grows beside it and checks that each library is usable by an ordinary application, not only by Lictor.

Language and runtime

WebGlue is written in Praxis and runs on the BEAM today: namespaces compile to .beam modules, long-lived state lives in OTP processes, and pub/sub runs on Erlang’s pg. Established BEAM libraries — Bandit, Plug, WebSock, Redix — are used through Praxis’ interop. One deliberate exception builds with something other than the BEAM: Tailwind’s standalone CLI, fetched pinned and checksummed like a dependency.

The frontend half is planned for Praxis’ browser target, and its design was settled in October 2026: a reactive core of Praxis’ own that keeps Solid’s model — components run once, and only what read a changed value updates — but not Solid’s runtime, and no React target.

Where it stands

Version 0.8.0 (19 October 2026) has twenty-one libraries and the demo, all implemented and tested — 872 tests, every documented example among them — covering the contract, routing, two HTTP servers, templates, content negotiation, form bodies, CSRF, sessions, static files, a Tailwind build, WebSockets, pub/sub, presence, clustering, Redis, server push, validation and a data-layer seam. Everything is marked experimental. The backend is the focus; the frontend follows.