Skip to content

Run and shutdown

app.run() options, NodeRunHandle.stop(), and graceful drain on Node.js.

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

On Node, host.build() returns an app with run(). Calling run() starts the server and returns a NodeRunHandle you use to stop gracefully.

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();
const handle = app.run({ port: 3000, host: "0.0.0.0", shutdownTimeout: 10_000 });

run() accepts port, host, and shutdownTimeout. Each falls back to host.config.host and then framework defaults (3000, "localhost", 10000 ms).

Startup order matters: singleton onStart() hooks run before the port opens, so warm-up finishes before the first request arrives.

Gate traffic on host.state:

host.http.get("/ready", { isolated: true }, () =>
host.state === "ready"
? new FlareResponse(200, { state: host.state })
: new FlareResponse(503, { state: host.state }),
);

See Lifecycle for the full state machine.

Call handle.stop() to drain without exiting the process. SIGTERM and SIGINT run the same drain, then exit the process:

await handle.stop();

While draining, host.state is "draining". New requests get 503; open WebSockets are closed with a 1001 going-away frame, and in-flight requests finish within shutdownTimeout before any remaining connections are force-closed. Then onStop() hooks run and state reaches "stopped".

A hand-rolled handler that stops at server.close() misses most of this: that call only stops new connections, so open WebSockets keep the process alive until the orchestrator’s kill timer ends it mid-request.

Workers do not call run(). Export app.export() and let the platform invoke fetch per request. See Cloudflare Workers.