Skip to content

Register a custom handler

Map a thrown FlareError to the HTTP response you want with host.http.error and let everything else fall through.

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

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.

  1. Register a function handler with host.http.error((err, context) => ...).
  2. Narrow the thrown error (for example err instanceof FlareError && err.category === "not_found").
  3. Return a FlareResponse to 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();
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();
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();
});

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.

Contract validation failures return 400 or 413 before the pipeline. They never reach host.http.error(). See HTTP contracts and Error handling overview.