Install
Packages, project layout, configuration, and the runtime entry points.
Two packages and an optional flare.json get you a Flare app ready to build() and run on Node or Cloudflare Workers.
Packages
Section titled “Packages”pnpm add @flare-ts/core @flare-ts/libnpm install, yarn add, and bun add work the same.
@flare-ts/core is the framework entry point: host, HTTP arc, DI, config. Import most symbols from there. Add @flare-ts/lib when you import schema helpers directly, for example schema() from @flare-ts/lib/schema. Entry points and subpaths for both packages are in the API Reference.
@flare-ts/lib has no runtime dependencies, and @flare-ts/core depends only on @flare-ts/lib, so Flare pulls no third-party packages into your node_modules.
Project layout
Section titled “Project layout”A minimal Flare project is one entry module plus an optional flare.json:
my-app/├── src/│ └── main.ts # FlareHost, routes, host.build(), then run() or export()├── flare.json # optional: port, logging, custom config sections├── package.json # "type": "module"└── tsconfig.jsonFlare is ESM only, so the project must be too: package.json needs "type": "module". npm init does not add it, and without it Node parses src/main.ts as CommonJS and the first import fails with Cannot use import statement outside a module.
Run the entry file with Node directly:
node src/main.tsNode runs TypeScript natively since 22.18, with one gap that matters here: decorators. An app that registers routes inline (host.http.get(...)) runs under plain node. Controller classes register routes with decorators (@Get, @Post), which Node can’t parse, so run a controller app with a TypeScript runner instead:
npx tsx src/main.tsThe runner is also the answer on Node versions below 22.18.
Configuration
Section titled “Configuration”flare.json at the project root is optional, and every field has a built-in default. On Node it’s read once at host.build(). Cloudflare Workers have no filesystem, so there the same values are passed to the adapter in code (see Runtimes below).
{ "host": { "port": 4000 }, "log": { "level": "debug", "format": "pretty" }}Override any field per environment with a double-underscore variable: FLARE__host__port=4000, FLARE__log__level=warn.
Configuration explains the model: typed sections, flareConfig(...) for your own config, validation at build(). The flare.json reference documents every field, its default, the resolution order, and the override rules.
Runtimes
Section titled “Runtimes”The same registrations run on both supported runtimes. What changes is the adapter you hand FlareHost and the entry-point call on the built app.
| Runtime | Adapter | Entry point |
|---|---|---|
| Node.js ≥ 22 | import { node } from "@flare-ts/core/node" | app.run() |
| Cloudflare Workers | import { cf } from "@flare-ts/core/cloudflare" | export default app.export() |
| Bun / Deno | @flare-ts/core/bun, @flare-ts/core/deno | Stubs: host.build() throws today |
Node.js
Section titled “Node.js”import { FlareHost, FlareResponse } from "@flare-ts/core";import { node } from "@flare-ts/core/node";
const host = new FlareHost(node);host.http.get("/health", () => new FlareResponse(200, { ok: true }));
const app = host.build();app.run();build() reads flare.json; run() binds the port (3000 by default) and serves. Run and shutdown covers ports, readiness, and graceful drain.
Cloudflare Workers
Section titled “Cloudflare Workers”// @ground:skipimport { FlareHost, FlareResponse } from "@flare-ts/core";import { buildCf } from "@flare-ts/core/cloudflare";import flareJson from "../flare.json" with { type: "json" };
const host = new FlareHost(buildCf(flareJson));host.http.get("/health", () => new FlareResponse(200, { ok: true }));
const app = host.build();export default app.export();Workers never read a file at runtime, so flare.json is imported and bundled into the Worker instead: buildCf(flareJson) hands those values to the host at build(). Pass the Worker env as a second argument, buildCf(flareJson, env), when you also want FLARE__* overrides applied. The bare cf adapter carries no configuration at all, which is fine when the defaults are.
Enable nodejs_compat in wrangler.toml; Flare’s logger uses AsyncLocalStorage, and the Worker fails to start without the flag. One surface differs by design: host.singleton doesn’t exist on a Cloudflare host, so register per-request services with host.scoped(). Cloudflare Workers covers deploy, bindings, secrets, and Durable Objects.
Bun and Deno
Section titled “Bun and Deno”The adapters exist so import paths stay stable, but host.build() throws on both today. See Bun and Deno.