Skip to content

Host extensions

defineHostExtension lets packages register services, config, and routes and expose typed members on FlareHost.

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

Host extensions let a package contribute to the composition graph and expose typed members on FlareHost when you pass the extension in the constructor array. One extension can register config, scoped services, and HTTP routes during install, then return any number of named members (host.db, host.migrate(), and so on) with full TypeScript inference.

Without extensions, every integration is copy-pasted registration in the app entry file: host.cfg(...), host.scoped(...), host.http.controller(...), plus a separate import for the package’s query API. Extensions colocate that wiring inside the package and type the result onto the host so callers get autocomplete and compile-time checks.

Passing the extension is the opt-in. There is no second activation call after construction.

defineHostExtension returns a descriptor whose install function runs once when you construct the host. The installer receives a narrow HostExtensionContext: scoped, cfg, and http only. It cannot reach lifecycle state, build internals, or test controls.

Everything registered through the context compiles like author code: services enter the DI graph, routes enter the HTTP arc. There is no per-request extension hook on the hot path.

import { defineHostExtension, flareConfig, FlareHost } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
import { str } from "@flare-ts/lib/schema";
const PLUGIN_CONFIG = flareConfig("myPlugin", { apiKey: str });
export function myPlugin(opts: { apiKey: string }) {
return defineHostExtension((host) => {
host.cfg(PLUGIN_CONFIG);
return {
greet: () => `configured with ${opts.apiKey}`,
};
});
}
const host = new FlareHost(node, [myPlugin({ apiKey: "dev" })]);
host.greet();

new FlareHost(adapter, [extA, extB]) intersects every extension’s member map onto the host type. An inline array infers tightly on its own; you only need as const if you hoist the extensions into a separate const variable first.

Members are plain values: functions, objects, or facades. The drizzle-style pattern in the API docs shows returning both a db API and a migrate helper from one installer.

The installer runs at host construction, not per request. Extensions cannot:

  • Add middleware or logic that runs on every request outside the normal compiled pipeline
  • Read or mutate host.state, host.build(), or test-mode controls
  • Register singletons through a hidden API (only scoped, cfg, and http on the context)

If you need request-time behavior, register middleware or services through the http or scoped members of the narrow HostExtensionContext the installer receives, not the full host, and inject them in handlers like any other registration.

The Cloudflare adapter stamps its own members (for example durableObject) onto the host through the adapter’s extendHost hook, a separate path from the constructor extensions this page covers. Worker-only surfaces stay on the Cloudflare entry path; see Durable Objects.