Skip to content

The arc model

An arc is Flare's transport-facing capability on the host. HTTP and WebSocket routes share services and config.

AI generated, pending review Updated 10 days ago · Flare 0.3

An arc is a transport-facing capability registered on FlareHost. Each arc owns how work enters the process (routing, protocol hooks, request-scoped machinery) and how results leave. Business logic stays in FlareService classes and shared config, which the host resolves once and makes available to every arc.

The host exposes these arcs today:

ArcRegistration surfaceWhere it runs
HTTPhost.httpNode, Workers
WebSockethost.wsNode, Workers

Coupling domain code directly to HTTP means rewriting services when you add a second transport. Flare separates the two:

LayerOwns
ArcTransport (paths, protocol middleware, arc-local lifecycle)
HostDI container, config, logger, composition, validation

A FlareService written for an HTTP controller can be injected from a WebSocket handler without changes. The arc supplies the entry point; the service supplies the logic.

The HTTP arc is everything under host.http. That’s where you register inline routes and class-based controllers, global and group-scoped middleware, prefixed route groups, central error mapping, arc-wide CORS, and the arc’s own start and stop hooks.

For the full method surface, see Routing and Host.

The WebSocket arc lives under host.ws. Register inline handlers with host.ws.route(path) and controller classes with host.ws.controller(path, Cls). Messages are typed with socketContract; parsed upgrade params and inbound messages arrive on scope.input the same way HTTP inline handlers read route, query, and body fields.

See WebSockets for routes, contracts, channels, and connection state.

For each matched HTTP route, the compiled pipeline runs middleware and the handler in this order:

  1. Global and group before hooks (globals first, then group-local; registration order within each scope). See Middleware for how globals and groups interleave.
  2. Route handler (with contract body parsing immediately before the handler when the route declares a body)
  3. after hooks (globals first, then group-local; registration order within each scope)
  4. finally hooks (LIFO across the combined middleware list)

Route and query contract values are parsed on the request before any before hook runs so middleware can read typed ctx.req route and query fields. Body contracts run after the before chain and immediately before the handler so auth middleware can reject a request before the body is consumed.

Returning a non-undefined response from before (typically new FlareResponse(...)) skips the handler and all after hooks for that request. finally still runs. See Middleware for short-circuit rules.

Inline handlers read parsed route, query, and body values from scope.input. Controller methods with httpContract use ctx.extract(entry). See HTTP contracts.

host.build() finalizes the whole host in one synchronous call and returns a runtime-specific app (run() on Node, export() on Workers, test() when FLARE_MODE=test). A second call returns the cached app with no extra work.

For the arcs, build() compiles each arc’s routes into per-route pipelines (middleware ordering, router) and validates every arc registration. Arc compile failures, including a route or middleware whose static state token no earlier before() hook provides, throw a plain Error with a descriptive message. For the ordered steps build() runs across the whole host, see What build() does; for how state provisioning is checked across a route’s chain, see Build-time validation.

Callbacks registered with host.http.onStart() and host.http.onStop() stay on the arc; the returned app’s start() / stop() (or async variants) invoke them in registration order alongside singleton service lifecycle.