← index

[Web]App

inherits Object · defined at web/04-app.qn:70

The framework core: routing DSL, middleware onion, render conventions, and error mapping over a pure handler pipeline — the [HTTP]Server is merely the transport.

use std:net/http_server;
use std:web/*;
var app = [Web]App.new;
app.get:'/' do:{ 'hello from Quoin' };
app.get:'/users/:id' do:{ |req| users.at:(req.param:'id') };   "* Map -> JSON
app.use:{ |req next| next.value:req };                         "* middleware
app.serve:':8080'                                              "* start + join (blocks)

Handler blocks may take the request as a parameter ({ |req| … }) or address it as self ({ .param:'id' }); the return value is normalized by the respondTo: conventions. app.handle:req is the whole app as a pure function — request in, response out, no sockets — so apps unit-test in-process. See docs/internal/WEB_ARCH.md.

Instance methods

debug:

Include error detail in 500 bodies (a development aid — off by default).

web/04-app.qn:111

delete: do:

Register handler h for DELETE requests matching pattern; returns self.

web/04-app.qn:95

dispatch: pool:

The transport-side hop: debug observability is answered HERE (the main VM — its psTree sees the whole topology: connection tasks, pool workers, per-worker states), request logging too (it sees request, response, and elapsed time in both the in-VM and pool paths, and the pure handle: stays silent for unit tests), and everything else goes to the pool or the in-VM pipeline.

web/04-app.qn:262

get: do:

Register handler h for GET requests matching pattern (':name' and '*splat' segments capture into req.params — see [Web]Route); returns self.

app.get:'/users/:id' do:{ |req| users.at:(req.param:'id') }

web/04-app.qn:87

handle:

Run one request through the whole app — middleware onion -> router -> handler -> respondTo: normalization — with HttpError mapped to its status (+ optional body) and anything else to a 500. Always returns an [HTTP]ServerResponse; never throws. Pure (no sockets), so an app unit-tests in-process:

var app = [Web]App.new;
app.get:'/hello/:name' do:{ |req| 'hi ' + (req.param:'name') };
var req = [HTTP]ServerRequest.new:{ var method = 'GET'; var target = '/hello/qn' };
(app.handle:req).body.asString   "* -> hi qn

web/04-app.qn:132

init

Internal: an empty router and middleware chain, debug off, request logging on.

web/04-app.qn:73

logRequests:

Log each SERVED request through Log.info: (on by default): method, target, status, elapsed time — e.g. GET /users/7 -> 200 (1ms 200µs). Lives on the transport hop, so the pure handle: stays silent (unit tests don't log); Log.level:/Log.sink: govern the lines like any other entry. logRequests:false turns it off; returns self.

web/04-app.qn:118

on: pattern: do:

Register handler h for an arbitrary verb (e.g. 'OPTIONS'); returns self.

web/04-app.qn:97

patch: do:

Register handler h for PATCH requests matching pattern; returns self.

web/04-app.qn:93

pool

The [Web]Pool when serving in pool mode, else nil.

web/04-app.qn:252

poolMain:

Declare this VM a pool HOST even though it runs inside a worker; returns self. Worker.worker? alone conflates "pool worker" with "any worker": a worker that legitimately HOSTS a pool (the soak spawned from the REPL, any nested-fleet unit) must declare itself with poolMain:true before its serve call, or that call would park waiting for a pool sentinel that is never coming.

web/04-app.qn:218

poolStop

Stop the worker pool, if any; returns self.

web/04-app.qn:254

poolWorkerLoop

The worker side of the same-unit model: verify the pool sentinel (pre-buffered at spawn — its absence means this worker was NOT spawned by a web pool, e.g. someone ran serve:workers: inside their own worker), then serve request data until stopped. Each request runs as its own Task, so handlers doing I/O overlap within a worker exactly as connection tasks do in the single-VM mode.

web/04-app.qn:283

poolWorkerLoopWith:

The sentinel-verified entry, for callers that already received the worker's first message (e.g. a mode-dispatching unit like the web soak, which answers 'soak' itself and forwards anything else here).

web/04-app.qn:288

post: do:

Register handler h for POST requests matching pattern; returns self.

web/04-app.qn:89

put: do:

Register handler h for PUT requests matching pattern; returns self.

web/04-app.qn:91

respondTo:String
respondTo:Map
respondTo:List
respondTo:Integer
respondTo:Bytes
respondTo:Generator
respondTo:[HTTP]ServerResponse
respondTo:

Normalize a handler's return value into an [HTTP]ServerResponse (a type-directed multimethod): String -> text/plain, Map/List -> JSON, Integer -> a bare status, Bytes -> application/octet-stream, Generator -> a chunked stream, a response passes through, nil -> 404, anything else -> the text of its .s. Add app-specific variants by subclassing [Web]App and extending the multimethod.

web/04-app.qn:183

route:

One routed dispatch (the innermost layer of the onion): bind the most specific route's params, run its handler, normalize. A path other verbs serve -> 405 with Allow; an unroutable path -> 404; a malformed percent escape in the path -> 400.

web/04-app.qn:161

router

The [Web]Router holding the per-verb route tables.

web/04-app.qn:99

serve:

Serve address on the calling task, blocking: start: then join.

web/04-app.qn:207

serve: workers:

Blocking pool serve: start:workers: (thread-backed) then join.

web/04-app.qn:244

serve: workers: backing:

Blocking pool serve with an explicit backing ('thread' | 'process'): start:workers:backing: then join.

web/04-app.qn:247

start:

Bind address (e.g. ':8080'; ':0' for ephemeral) and serve in the background, returning the [HTTP]Server handle (port / stop / join / close — tests, graceful shutdown).

web/04-app.qn:199

start: workers:

start: with the pure pipeline on a pool of n worker isolates, thread-backed (see start:workers:backing:).

web/04-app.qn:211

start: workers: backing:

start: with the pure pipeline running on a pool of n worker isolates (docs/internal/CONCURRENCY_ARCH.md §13): the transport VM keeps the sockets and ships requests as data; each pool worker re-runs THIS app's unit (VM.unit — the same-unit provisioning model), whose serve call lands in poolWorkerLoop below instead of binding. backing is 'thread' or 'process' — 'process' escapes the same-process scheduling ceiling for CPU-heavy handlers. Constraints, by design: handlers in pool mode cannot capture main-VM mutable state (isolates share nothing), and Generator bodies materialize. workers:0 = the single-VM path, unchanged; needs a unit-run program (throws under the REPL / -e).

web/04-app.qn:230

use:

Install middleware mw; returns self. Onion model: the first use: is outermost. mw is called with (req, next) — a two-param block, or anything responding to valueWithArgs: — where next is a one-arg block producing the inner response. Returning without calling next short-circuits; either way the return value goes through the respondTo: conventions.

web/04-app.qn:108