Register a custom handler
Map a thrown FlareError to the HTTP response you want with host.http.error and let everything else fall through.
You throw a FlareError in a route and you want the HTTP response shaped your way: a specific status, a specific JSON body, maybe a log line first. Register a handler with host.http.error(...). It inspects the thrown error and returns the response you want, or returns nothing and lets the next handler (or the default) take it.
You still throw errors from a flareErrorCodes registry; the handler only customizes how a thrown error becomes a response.
- Register a function handler with
host.http.error((err, context) => ...). - Narrow the thrown error (for example
err instanceof FlareError && err.category === "not_found"). - Return a
FlareResponseto override, or return nothing to defer.
import { FlareError, FlareHost, FlareResponse } from "@flare-ts/core";import { flareErrorCodes } from "@flare-ts/core/errors";import { node } from "@flare-ts/core/node";
const UserErrors = flareErrorCodes({ not_found: { UserNotFound: { expose: true, code: 1001 }, },});
const host = new FlareHost(node);
host.http.error((err, context) => { if (err instanceof FlareError && err.category === "not_found") { return new FlareResponse(404, { error: err.name, requestId: context.requestId, }); }});
host.http.get("/users/missing", () => { throw new FlareError(UserErrors.not_found.UserNotFound);});
host.http.get("/users/u1", () => new FlareResponse(200, { id: "u1" }));
const app = host.build();app.run();Class form with logging
Section titled “Class form with logging”import { ErrorHandlerBase, FlareError, FlareHost, FlareResponse, Logger,} from "@flare-ts/core";import type { HttpErrorContext, ResponseLike } from "@flare-ts/core";import { node } from "@flare-ts/core/node";
class HttpErrors extends ErrorHandlerBase { public static override deps = [Logger]; readonly #log = this.inject(Logger);
override handle(err: FlareError | Error, context: HttpErrorContext): ResponseLike | void { if (err instanceof FlareError && err.category === "conflict") { this.#log.warn("conflict", { code: err.name, requestId: context.requestId }); return new FlareResponse(409, { error: err.name }); } }}
const host = new FlareHost(node);host.http.error(HttpErrors);
const app = host.build();app.run();Scope to one group
Section titled “Scope to one group”host.http.group("/api", (g) => { g.error((err) => { if (err instanceof FlareError && err.category === "unauthorized") { return new FlareResponse(401, { error: err.name }); } }); g.get("/secret", () => new FlareResponse(200, { ok: true })); return g.register();});Default fallback
Section titled “Default fallback”You don’t have to register anything. A thrown FlareError with no matching handler maps automatically by category. Any other Error becomes 500. Register handlers only for errors you want to shape differently.
What error handlers don’t catch
Section titled “What error handlers don’t catch”Contract validation failures return 400 or 413 before the pipeline. They never reach host.http.error(). See HTTP contracts and Error handling overview.