Skip to content
These docs describe @green-tea/core 26.9.0-beta.1what changed. Install with npm i @green-tea/core@beta.

createApp options

const app = createApp(options);
Option Type Default Meaning
modules Ctor[] the @Module classes to wire (required)
plugins Plugin[] [] plugins, each limited to bus.on + scope.add + onShutdown (plugins)
hooks Hooks[] [] lifecycle participation without extending the graph; { onShutdown } today (teardown)
limits RequestLimits see below body-size, concurrency, and timeout ceilings
devGraph boolean false mount GET /__graph__ (introspection)
devOpenapi boolean false mount GET /__openapi__ serving the OpenAPI document
overrides Record<string, unknown> swap any node by token (testing)
tls TlsOptions serve over https/wss
trustProxy boolean false honor X-Forwarded-* for ctx.protocol / ctx.ip
security boolean | SecurityOptions true secure-by-default headers (security)
cors CorsOptions CORS handling
bodyDuplicates 'array' | 'last' 'last' policy for repeated form fields
onError ErrorRenderer render errors your way (error handling); returns a response or undefined to fall back to JSON
views string process.cwd() base dir @Html('file.html') paths resolve against — relative paths join it, absolute paths are used as-is (HTML & views)
viewEngine (source: string, data: unknown) => string built-in render swap in your own template engine for @Html(..., { template: true }); template mode only (HTML & views)
static boolean | string serve a directory as a GET/HEAD fallback (after declared routes, before 404); true./public, a string → that dir; path-traversal-safe; needs a filesystem — Node/Deno/Bun only (HTML & views)
logger Logger structured JSON (readable on a TTY) where every framework diagnostic is written; also injectable as @needs('logger') (observability)
logRequests boolean false log one line per completed request and per failure; a removable Bus subscriber, not a middleware (observability)
shutdownTimeoutMs number 10_000 how long close() gives in-flight work before forcing the rest shut; close({ timeoutMs }) still wins per call. Set it here when close() is reached from a signal handler or shutdown hook you do not own (runtimes)
teardownTimeoutMs number milliseconds reserved out of shutdownTimeoutMs for teardown, so a slow drain cannot leave a connection unclosed. Unset, the drain may use the whole budget and teardown takes what is left. Larger than shutdownTimeoutMs throws at boot (teardown)
handleSignals boolean false register SIGINT/SIGTERM to run close() and exit, using each runtime’s own API (teardown)
mesh MeshConfig distributed DI — requires experimental: true (mesh)
experimental boolean false opt in to alpha features (currently gates mesh)
warnGraphDepth number | false 20 warn when one route resolves to more than this many steps; false disables the design warning
Field Default
maxBodyBytes 1_000_000 (→ 413 when exceeded)
maxConnections 1000 (Node only; <= 0 means unlimited)
maxConcurrentRequests unlimited (<= 0 also means unlimited); bounds concurrently executing handlers, and a request above that ceiling → 503 with Retry-After: 1
requestTimeoutMs 30_000
headersTimeoutMs 10_000
keepAliveTimeoutMs 5_000
maxParts 1000 (multipart)

maxConcurrentRequests is enforced before request-body acquisition in both the Node and Fetch paths, so it applies across Node, Deno, Bun, and edge — as a budget per server or Fetch adapter instance, not one ceiling shared by the app (runtimes). Long-lived streams release their slot once request handling returns, and WebSocket upgrades are outside this budget.

{ key, cert, ca?, passphrase? }key/cert are Buffer | string.

{ origins, methods?, allowedHeaders?, exposedHeaders?, credentials?, maxAge? }. origins is a string, array, '*', or a (origin) => boolean predicate. credentials: true is never combined with '*'.

{ hsts?, frameOptions?, referrerPolicy?, noSniff?, dnsPrefetchControl?, csp? }. HSTS is only emitted on secure connections. csp is opt-in.

{ secret?, teapots?, timeoutMs?, heartbeatMs?, reconnect?, onManifestChange?, bootTimeoutMs? }teapots is { url, secret }[]; timeoutMs bounds an RPC (default 30s, → 504), heartbeatMs is the ping gap that detects a half-open link (default 15s, → 503). reconnect is boolean | { initialDelayMs?, maxDelayMs? } (default on; 500ms doubling to 30s with jitter), onManifestChange is 'refuse' — the default, and the only policy today — and bootTimeoutMs is how long boot waits for a teapot that has not started yet (default timeoutMs; 0 for a single attempt). A refused handshake is never retried. See Mesh.

createApp returns an App:

Member Returns Notes
listen(port) Promise<http.Server> boots providers, then serves
close({ timeoutMs? }) Promise<void> drains in-flight, closes streams + mesh links, then runs registered teardown (dispose(), onShutdown); after the deadline (default 10s, or createApp({ shutdownTimeoutMs })) it warns and force-closes what is left. Draining is Node-only — on Deno and Bun use the server serveDeno()/serveBun() returned, which also runs teardown (runtimes)
ready() Promise<void> resolves the graph and mesh links without booting providers
fetch(request) Promise<Response> Web-Standards HTTP/SSE handler for Node, Deno, Bun, and edge
upgrade(request, socket) Promise<void> neutral WebSocket upgrade used by non-Node adapters
inspect(route) InspectLine[] the provider/step/handler chain for a route
explain(route) Explain the chain annotated with each node’s needs/provides
graph() GraphView the full node + route graph
toMermaid() / toDOT() string render the graph
openapi(info?) OpenApiDoc structural OpenAPI 3.1 document (details)
degraded() string[] optional providers running degraded (empty until providers boot through fetch, upgrade, or listen)
bus Bus lifecycle + request event bus (observability)
logger Logger the app’s logger — the one passed in, or the default