Skip to content

Authoring styles

Flare HTTP handlers come in two shapes: inline functions on host.http and class-based controllers. Both share the same pipeline.

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

Flare registers HTTP handlers in two styles. Most apps use both: inline functions for one-off routes, and ControllerBase subclasses when several routes share a prefix, contract, services, or state tokens.

StyleRegistrationBest for
Inline functionhost.http.get(path, handler) or host.http.get(path, options, handler)Single routes, quick endpoints, functional middleware hooks
Controller classhost.http.controller(prefix, Cls) with @Get, @Post, … decoratorsResource groups, shared static contract, CRUD surfaces

Middleware and error handlers follow the same split: builder callbacks on host.http.before / after / finally / error, or classes extending MiddlewareBase / ErrorHandlerBase registered with host.http.use / host.http.error.

Inline route handlers are functions with signature (ctx, scope) => HandlerResult. They receive a FlareHttpContext as ctx and a handler scope as scope.

Declare services with a named inject map (inject: { db: DbService }, read scope.db). Declare route, query, and body shapes as inline descriptor fields on the route options (route, query, body) and read parsed values from scope.input.

import { FlareHost, FlareResponse } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
import { int } from "@flare-ts/lib/schema";
const host = new FlareHost(node);
host.http.get("/ping", () => new FlareResponse(200, { ok: true }));
host.http.get("/users/:id", { route: { id: int } }, (_ctx, scope) => {
return new FlareResponse(200, { id: scope.input.route.id });
});
const app = host.build();
app.run();

See Inline route handlers for the full HttpRouteOptions surface.

Controllers group routes under one mount prefix. Each class declares static deps, static state, and optionally static contract (an httpContract token). Handler methods use decorators from @flare-ts/core/decorators and read validated input with this.ctx.extract(Contract.entry).

import { ControllerBase, FlareHost, httpContract } from "@flare-ts/core";
import { Get } from "@flare-ts/core/decorators";
import { node } from "@flare-ts/core/node";
import { int } from "@flare-ts/lib/schema";
const UsersContract = httpContract({
show: { route: { id: int } },
});
class UsersController extends ControllerBase {
public static override deps = [];
public static override state = [];
public static override contract = UsersContract;
@Get("/:id")
show() {
const { route } = this.ctx.extract(UsersContract.show);
return this.ok({ id: route.id });
}
}
const host = new FlareHost(node);
host.http.controller("/users", UsersController);
const app = host.build();
app.run();

See Controller classes for static members, decorators, and response helpers.

HTTP contracts attach two ways:

  • Inline routes: descriptor fields on the route options (route, query, body, response, maxBodyBytes, signedCookies), or a branded contract token (not both).
  • Controllers: static contract = httpContract({ methodName: descriptor, … }) and this.ctx.extract(Contract.methodName) inside each handler.
ConcernInlineClass
Middlewarehost.http.before(fn) or host.http.before(options, fn)host.http.use(MiddlewareCls)
Error handlinghost.http.error(fn) or host.http.error(options, fn)host.http.error(ErrorHandlerCls)

Group-scoped registration uses the same APIs on the group builder inside host.http.group. See Middleware and Error handling.

Use inline handlers when the route is small, standalone, or you are prototyping. Use a controller when you have multiple methods on one resource, a shared httpContract, or you want this.ok / this.notFound helpers without constructing FlareResponse yourself.

Both styles compile to dedicated per-route execution functions at build(). The choice is about structure and compile-time guardrails (static deps, static state, contract keys matched to method names).

Without a shared surface for this, the controller shape is something each codebase reinvents: a base class, a way to map methods to routes, a set of response helpers, and the standing hope that a route written as a class runs the same middleware and validation as one written as a function. Here both shapes are first-class over one pipeline, so a class handler and an inline handler with the same contract behave identically, and promoting a route from a function to a controller method is a change of structure rather than of runtime behavior.