Liking cljdoc? Tell your friends :D

dev.arkaitz.web-base

Public entry point of web-base: the handler that wires the base together and the server lifecycle. A library the host calls; it never calls the host back except through the functions the host hands it (SPEC §3).

The wiring convention IS the product, so here it is, outermost first:

request-id → security headers → proxy (opt-in) → body limit → [/wb/ assets → sessionless routes → host static → ] error boundary → session → params → i18n → multipart (declared routes only) → csrf → subject → ring-handler router, per matched route: error → gate → render → coercion → handler default handler: 404 / 405 / nil-handler 500

Static assets answer before the session: a stylesheet fetch must not mint a session cookie, and a cookie on an asset defeats shared caches. They still carry the request id and the security headers. So do the host's :sessionless routes — a health probe, a webhook — which read no session and write none, whatever cookie arrives. The body limit sits outside all of them, so nothing reads more than it allows; multipart sits outside csrf because the token is one of its fields, and inside i18n so a refused upload's page speaks the negotiated language. Session sits outside subject (the subject function reads the session) and outside csrf (the token lives in the session); params sits outside csrf because the token may arrive as a form field; i18n sits outside csrf so a refused request's error page speaks the negotiated language. Error sits outside gate and render inside the router: a predicate or a layout may throw, and a refusal is an error datum. The default handler runs outside router middleware, so the request id, the session and the security headers reach it from the outer stack while errors render on their own.

Public entry point of web-base: the handler that wires the base together and
the server lifecycle. A library the host calls; it never calls the host back
except through the functions the host hands it (SPEC §3).

The wiring convention IS the product, so here it is, outermost first:

  request-id → security headers → proxy (opt-in) → body limit
  → [/wb/ assets → sessionless routes → host static → ] error boundary → session → params
  → i18n → multipart (declared routes only) → csrf → subject
  → ring-handler
      router, per matched route: error → gate → render → coercion → handler
      default handler: 404 / 405 / nil-handler 500

Static assets answer before the session: a stylesheet fetch must not mint a
session cookie, and a cookie on an asset defeats shared caches. They still
carry the request id and the security headers. So do the host's `:sessionless`
routes — a health probe, a webhook — which read no session and write none, whatever
cookie arrives. The body limit sits outside all of them, so nothing reads more than
it allows; multipart sits outside csrf because the token is one of its fields, and
inside i18n so a refused upload's page speaks the negotiated language. Session sits
outside subject
(the subject function reads the session) and outside csrf (the token lives
in the session); params sits outside csrf because the token may arrive as a
form field; i18n sits outside csrf so a refused request's error page speaks
the negotiated language. Error sits outside gate and render inside the
router: a predicate or a layout may throw, and a refusal is an error datum.
The default handler runs outside router middleware, so the request id, the
session and the security headers reach it from the outer stack while errors
render on their own.
raw docstring

expandclj

(expand config) → the plain config its :plugins stand for, merged by the rules of dev.arkaitz.web-base.plugin — what handler builds from, to read at the REPL.

`(expand config)` → the plain config its `:plugins` stand for, merged by the rules of
`dev.arkaitz.web-base.plugin` — what `handler` builds from, to read at the REPL.
sourceraw docstring

form-doneclj

(form-done request location fragment)

The answer to a form the host accepted, for a page that works with and without JavaScript: an htmx swap gets fragment with a 200, and a navigation a 303 to location (response/see-other), so a reload never posts twice.

The answer to a form the host accepted, for a page that works with and without
JavaScript: an htmx swap gets `fragment` with a 200, and a navigation a 303 to
`location` (`response/see-other`), so a reload never posts twice.
sourceraw docstring

handlerclj

(handler config)

Builds the Ring handler from the host's config:

:routes reitit route data; per route :wb/layouts and :wb/gate, both inherited by nested routes (layouts concatenate, a child's gate replaces); :wb/log-path :template logs the route's template instead of its path, for a path that carries a secret; :wb/multipart {:max-file-size n …} parses a file upload for that route alone :session {:key base64-or-bytes} or {:store s} (required) :subject-fn request → subject or nil (default: always nil) :login-path where a refusal without a subject goes (required iff a route has :wb/gate) :coercion a reitit coercion, passed through (optional) :static create-resource-handler options for the host's assets (optional) :error-layout slot function for error pages (optional) :i18n {:dict … :default-locale … :locales […] :locale-fn …} (optional; :locales since 0.12.0, the default alone when absent) :security {:frame-options … :csp … :hsts {:max-age seconds} :proxy-hops n} (optional) :csrf false to disable the anti-forgery token (on for anything else, nil included) :sessionless {"/health" handler "/api/" handler} — answered before the session, CSRF, i18n and subject, with the request id, security headers and body limit only; a path ending in / takes everything under it, an exact path winning; a handler is a function or a var; a path or prefix that covers one of :routes is refused, and so is / (optional) :max-body-bytes the largest request body read, 200 000 by default; a route's :wb/multipart sets its own (optional) :assets [{:path "/name/" :root "classpath/prefix"}], served beside /wb/, before the session (optional) :stylesheets ["/app.css"], linked by the shell after the base's own (optional) :plugins values that contribute these same keys, merged by expand (optional)

Unknown keys are the host's own business — all but the names above, :assets, :stylesheets and :plugins among them since 0.11.0. Inside the maps the base owns — :session, :security and its :hsts, :i18n, :static — an unknown key is refused, naming its path. Every failure of a required or malformed value is raised here, at construction.

Builds the Ring handler from the host's config:

  :routes       reitit route data; per route `:wb/layouts` and `:wb/gate`, both
                inherited by nested routes (layouts concatenate, a child's gate replaces);
                `:wb/log-path :template` logs the route's template instead of its path,
                for a path that carries a secret; `:wb/multipart {:max-file-size n …}`
                parses a file upload for that route alone
  :session      `{:key base64-or-bytes}` or `{:store s}` (required)
  :subject-fn   request → subject or nil (default: always nil)
  :login-path   where a refusal without a subject goes (required iff a route has :wb/gate)
  :coercion     a reitit coercion, passed through (optional)
  :static       create-resource-handler options for the host's assets (optional)
  :error-layout slot function for error pages (optional)
  :i18n         `{:dict … :default-locale … :locales […] :locale-fn …}` (optional;
                `:locales` since 0.12.0, the default alone when absent)
  :security     `{:frame-options … :csp … :hsts {:max-age seconds} :proxy-hops n}` (optional)
  :csrf         false to disable the anti-forgery token (on for anything else, nil included)
  :sessionless  `{"/health" handler "/api/" handler}` — answered before the session,
                CSRF, i18n and subject, with the request id, security headers and body
                limit only; a path ending in `/` takes everything under it, an exact
                path winning; a handler is a function or a var; a path or prefix that
                covers one of :routes is refused, and so is `/` (optional)
  :max-body-bytes  the largest request body read, 200 000 by default; a route's
                `:wb/multipart` sets its own (optional)
  :assets       `[{:path "/name/" :root "classpath/prefix"}]`, served beside `/wb/`,
                before the session (optional)
  :stylesheets  `["/app.css"]`, linked by the shell after the base's own (optional)
  :plugins      values that contribute these same keys, merged by `expand` (optional)

Unknown keys are the host's own business — all but the names above, `:assets`,
`:stylesheets` and `:plugins` among them since 0.11.0. Inside the maps the base owns —
`:session`, `:security` and its `:hsts`, `:i18n`, `:static` — an unknown key is
refused, naming its path. Every failure of a required or malformed value is raised
here, at construction.
sourceraw docstring

redirect-forclj

(redirect-for request path): a 303 for a navigation, an HX-Redirect for an htmx swap, uncached — how the gate sends a refused visitor away, for a host's own detour.

`(redirect-for request path)`: a 303 for a navigation, an `HX-Redirect` for an htmx
swap, uncached — how the gate sends a refused visitor away, for a host's own detour.
sourceraw docstring

refuse-formclj

(refuse-form request path form fragment)

The answer to a form the host refused, for a page that works with and without JavaScript: an htmx swap gets fragment — the form with its errors, drawn by the handler — as a 422 (response/unprocessable), and a navigation gets the page at path rendered again with form under :wb/form (rerender, whose rules apply, path never taken from the request among them).

(if-let [errors (validate values)] (wb/refuse-form request "/things" {:values values :errors errors} (thing-form request values errors)) (wb/form-done request "/things" (thing-row saved)))

The answer to a form the host refused, for a page that works with and without
JavaScript: an htmx swap gets `fragment` — the form with its errors, drawn by the
handler — as a 422 (`response/unprocessable`), and a navigation gets the page at
`path` rendered again with `form` under `:wb/form` (`rerender`, whose rules apply,
`path` never taken from the request among them).

  (if-let [errors (validate values)]
    (wb/refuse-form request "/things" {:values values :errors errors}
                    (thing-form request values errors))
    (wb/form-done request "/things" (thing-row saved)))
sourceraw docstring

rerenderclj

(rerender request path form)

The page at path rendered again for the request a handler is answering, with form — whatever the host's view reads, typically {:values … :errors …} — under :wb/form, and status 422 when the page renders as an ordinary 200.

For a classic form that failed validation: the POST handler validates, and on failure answers (rerender request "/things" {:values v :errors e}), so the person gets the page they were on with what they typed and why it was refused — without a redirect that loses both, and without the POST handler rebuilding the page it does not own. The page's own :get handler runs, compiled as the router compiled it: its gate, coercion and layouts apply as for any GET of that path. The request keeps its session, subject, locale and CSRF token; its form, body and parameters are dropped, and path's own path and query parameters take their place. Views read (:wb/form request), whose shape is the host's.

A response the page answers with a redirect, an HX-Redirect or any status other than 200 is returned as it is.

On an htmx request the page renders as any GET does under htmx: its content without its layouts, which lands inside whatever the form targeted. A form that swaps only itself, and still works without JavaScript, answers through refuse-form instead.

Throws when path has no :get route, and when the request already carries :wb/form — a page that re-rendered into itself would never stop. Not a validation helper: the base still knows no schema (SPEC §7).

path is the host's own route, never input from the request. Whatever GET it names runs as a side effect of this POST, as the caller — a logout, a link's redemption — so a path taken from a form field would let whoever submits it choose which.

The page at `path` rendered again for the request a handler is answering, with
`form` — whatever the host's view reads, typically `{:values … :errors …}` — under
`:wb/form`, and status 422 when the page renders as an ordinary 200.

For a classic form that failed validation: the POST handler validates, and on
failure answers `(rerender request "/things" {:values v :errors e})`, so the
person gets the page they were on with what they typed and why it was refused —
without a redirect that loses both, and without the POST handler rebuilding the
page it does not own. The page's own `:get` handler runs, compiled as the router
compiled it: its gate, coercion and layouts apply as for any GET of that path. The
request keeps its session, subject, locale and CSRF token; its form, body and
parameters are dropped, and `path`'s own path and query parameters take their
place. Views read `(:wb/form request)`, whose shape is the host's.

A response the page answers with a redirect, an `HX-Redirect` or any status other
than 200 is returned as it is.

On an htmx request the page renders as any GET does under htmx: its content without
its layouts, which lands inside whatever the form targeted. A form that swaps only
itself, and still works without JavaScript, answers through `refuse-form` instead.

Throws when `path` has no `:get` route, and when the request already carries
`:wb/form` — a page that re-rendered into itself would never stop. Not a validation
helper: the base still knows no schema (SPEC §7).

**`path` is the host's own route, never input from the request.** Whatever GET it
names runs as a side effect of this POST, as the caller — a logout, a link's
redemption — so a path taken from a form field would let whoever submits it choose
which.
sourceraw docstring

startclj

(start handler {:port n}) → {:server s :port n}.

`(start handler {:port n})` → `{:server s :port n}`.
sourceraw docstring

stopclj

Stops the handle returned by start.

Stops the handle returned by `start`.
sourceraw docstring

subject-present?clj

The stock gate predicate.

The stock gate predicate.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close