Skip to content

What build() does

How host.build() validates your registrations and compiles a pipeline per route at startup, so wiring mistakes fail before any traffic.

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

The name build() is easy to misread. In most JavaScript projects, “build” means a bundler: esbuild, Vite, webpack, turning source files into an artifact you deploy. host.build() compiles, it doesn’t bundle. It doesn’t touch your files and it doesn’t produce an artifact. It runs at startup, reads the registrations you made on the host, and turns them into the thing that actually serves requests.

This page is about what that step does and when it runs. For the exact order of operations and the validator codes, see Build-time validation and Failure modes.

Before build(), a Flare app is a set of registrations on a FlareHost: config tokens, services, and transport surfaces (HTTP and WebSocket routes). That’s the application graph. Nothing is wired yet. The host is holding a description of what your app should be.

import { FlareHost, FlareResponse } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
const host = new FlareHost(node);
host.http.get("/ping", () => new FlareResponse(200, { ok: true }));
const app = host.build();
app.run();

That host.build() call reads the graph, checks it, and compiles a dedicated execution function for every route, covering its middleware, the handler, and its serializers. The returned app is the compiled output. app.run() on Node, or app.export() on Workers, runs it.

Two things, and both happen at startup, before the app serves anything.

It checks the whole graph. Missing service registrations, dependencies you declared but never registered, dependency cycles, and request-state tokens that no middleware provides are all caught here, when you call build(), not when a request happens to exercise that path. A broken HTTP boundary surfaces before traffic instead of on the first request that hits it.

It compiles each route ahead of time. Each route becomes one execution function. The request path doesn’t walk a list of decorators or rebuild a pipeline per request; that work already happened at build(). The cost is paid once, at startup, not on every request.

So the work build() does is whole-graph checking plus per-route code generation, and it does both at startup, inside your process. It is not an offline phase and it emits in-memory functions, not a file.

A bundler warning is something you can ignore and ship. build() does not work that way. When a validator finds a problem, build() throws, and the app is never returned, so the server never binds a port and the Worker never exports a handler. A wiring mistake fails before any traffic arrives.

That’s a deliberate trade. The alternative, a framework that starts anyway and fails on the first request that touches the broken path, moves the failure from your deploy logs to your users. Flare moves it the other way. If build() returns, the graph is coherent: every dependency resolves, every route has a pipeline, every state token has a provider.

Before build(), the host holds the application graph you registered. build() turns that graph into a running, validated program, checked on the way through. Registration is declarative and order among most registrations does not matter; nothing in the graph is inferred at request time. Once build() succeeds, the shape of your app is fixed and validated, and the request path is just running compiled functions.

build() is also safe to call again: a second call returns the same compiled app rather than recompiling. That matters in tests, where an entry module has already built the app by the time a test imports it.

  • Build-time validation: the exact order build() runs in and what each validator suite checks.
  • Failure modes: the full catalog of what throws, and when.
  • Host: the FlareHost registration surface and the runtime adapters.
  • The arc model: how a compiled route pipeline runs a request.