Route groups and versioning
Register v1 and v2 routes under shared prefixes with host.http.group and per-group middleware, CORS, and errors.
AI generated, pending review
You’re shipping a /v2 of your API while /v1 stays live, and you don’t want to repeat the prefix on every route. Put each version in its own host.http.group(prefix, …). The prefix lives in one place, and routes inside read like they always do.
- Call
host.http.group("/v1", (g) => { … })and register that version’s routes ong. - Do the same for
/v2in a second group. - Return
g.register()from each builder callback. That call finalizes the group and is required.
import { FlareHost, FlareResponse } from "@flare-ts/core";import { node } from "@flare-ts/core/node";
const host = new FlareHost(node);
host.http.group("/v1", (g) => { g.get("/health", () => new FlareResponse(200, { version: "v1" })); // GET /v1/health g.get("/users", () => new FlareResponse(200, { users: [] })); // GET /v1/users return g.register();});
host.http.group("/v2", (g) => { g.get("/health", () => new FlareResponse(200, { version: "v2" })); // GET /v2/health g.get("/users", () => new FlareResponse(200, { users: [] })); // GET /v2/users return g.register();});
const app = host.build();app.run();Every path registered on g is prefixed with the group’s prefix by string concatenation: g.get("/users", …) under /v2 mounts at /v2/users.
With controllers
Section titled “With controllers”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 UsersV1Controller 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, version: "v1" }); }}
class UsersV2Controller 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, version: "v2" }); }}
const host = new FlareHost(node);
host.http.group("/v1", (g) => { g.controller("/users", UsersV1Controller); // GET /v1/users/:id return g.register();});
host.http.group("/v2", (g) => { g.controller("/users", UsersV2Controller); // GET /v2/users/:id return g.register();});
const app = host.build();app.run();Per-version middleware and CORS
Section titled “Per-version middleware and CORS”The builder g exposes the same registration surface as host.http, scoped to the group:
import { FlareHost, FlareResponse, MiddlewareBase } from "@flare-ts/core";import { node } from "@flare-ts/core/node";
class LegacyAuthMiddleware extends MiddlewareBase { public static override deps = []; public static override state = [];
before() { if (!this.ctx.req.headers.get("authorization")) { return this.unauthorized({ error: "missing token" }); } }}
const host = new FlareHost(node);
host.http.group("/v1", (g) => { g.use(LegacyAuthMiddleware); // runs only for /v1/* routes g.cors({ origins: ["https://app.example.com"], credentials: true }); g.get("/users", () => new FlareResponse(200, { users: [] })); return g.register();});
const app = host.build();app.run();g.cors() fully replaces the arc-level CORS policy for routes in that group. It does not merge with host.http.cors().
Constraints
Section titled “Constraints”- Groups don’t nest. Use one group with a combined prefix (for example
/api/v1) instead of stacking groups. - Prefix join is literal. Both the group prefix and route paths must be valid absolute segments (
/api/v1+/users→/api/v1/users). g.isolated()skips global middleware for routes in the group. Register what you need withg.useor group-scoped hooks instead.
Related
Section titled “Related”- Routing overview: full registration surface
- Routing behavior: path composition and matching
- Enable CORS:
g.cors()semantics