Skip to content

Controller classes

Group related routes under ControllerBase with decorators, httpContract, and protected response helpers.

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

When several routes share a prefix, contract, services, or state tokens, promote them to a ControllerBase subclass and mount with host.http.controller(prefix, Cls).

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();

A controller class needs at least one decorated handler method. Mounting a class with none makes host.build() throw.

MemberRequired?Purpose
static depsYesServices this controller may inject ([] if none)
static stateYesState tokens any handler reads via ctx.state ([] if none)
static contractOptionalShared httpContract for this.ctx.extract
static configOptionalConfig tokens this controller may read via this.config
static isolatedOptionalWhen true, runs this controller’s routes with no global middleware (class form of the isolated route option)

Each handler method that calls this.ctx.extract(...) needs a matching key in static contract. The @Get("/:id") method above is named show, so the contract entry is show, not get.

MemberWhat you get
this.ctxThe FlareHttpContext for the in-flight request
this.ctx.extract(D)Typed { route, query, body } for the descriptor entry
this.inject(Token)A service from static deps
this.config(Token)A config section from static config

There is no this.req on a controller. Read request data from this.ctx.req or typed contract values from this.ctx.extract(...).

Handler methods inherit protected helpers from ControllerBase. Each returns a ResponseLike:

HelperStatus
ok(body)200
created(body)201
noContent()204
redirect(location, options?)302 default (301, 307, or 308 with options)
badRequest(body)400
unauthorized(body)401
forbidden(body)403
notFound(body)404
tooManyRequests(body)429
error(body)500

For return shapes beyond these helpers (plain objects, streams, FlareResponse), see Requests and responses.

@flare-ts/core/decorators exports @Get, @Post, @Put, @Patch, @Delete, @Head, @Options, and @Method(method, path?). Flare uses TC39 stage 3 decorators, not legacy experimentalDecorators.

@Get("") // controller root at the mount prefix
@Get("/:id") // prefix + "/:id"
@Post("")
@Delete("/:id")
@Method("GET") // path omitted → controller root

Named decorators require a path argument. Use "" for the controller root. Do not pass "/": the decorator throws at evaluation time.

Decorator paths follow the same rules as inline routes, except controller root routes use "" rather than "/".

Mount a controller inside a group with g.controller("/users", UsersController). The effective path is group prefix + controller prefix + decorator path. See Route groups and versioning.