$-prefixed handles.
Install
@forst/cli first. Add a postinstall script that runs forst generate ..
Run generate once before you import @forst/gen.
Details are on the Installation page.effect and enable Effect mode in ftconfig.json:
Promise mode
No extra dependency or generate setting is required.
Effect mode
@forst/gen/<package> (for example $auth from @forst/gen/auth) and call through it in both modes. See Generate a TypeScript client and Effect mode.
Quick start
1
Write a public Forst function
Only exported functions (capitalized names) are callable from JavaScript.
2
Install and generate
Follow Installation § Generated TypeScript client.Promise mode is enabled by default. For Effect return types, set
"generate": { "effect": true } in ftconfig.json before generating.3
Run a server
Development — hot reload while you edit Production — linked program binary for slim container images. Enable embedded invoke in With
.ft files:ftconfig.json before building:bridge.hostMode, the same binary runs your Forst entry and starts the Node app as a child. Set FORST_SKIP_NODE_HOST=1 on the same binary when Node runs separately and the binary should not spawn the Node host.For Go source inspection or hand go build, emit sources with generate.go flags:4
Set the URL and call
examples/in/rfc/embedded-invoke.
Configure the client
Promise mode creates a configured client object. Effect mode creates a layer that provides the generated package services:Custom HTTP headers
Pass default headers on the client config. They merge into every invoke and stream request. Per-call headers in the second argument override defaults for that request only.Content-Type: application/json is always set. The invoke authentication headers are reserved and cannot be replaced by your defaults or per-call headers. See Invoke security.
Connect-mode workers need the invoke HMAC secret from FORST_INVOKE_TOKEN (base64url). The forst dev process sets this when it starts. Sidecar spawn mode uses an inherited FORST_INVOKE_AUTH_FD handoff instead. See Invoke security.
How the client picks a server
Production never spawns a server. Set
FORST_BASE_URL (or FORST_INVOKE_URL / FORST_DEV_URL) explicitly. Local spawn of forst dev is opt-in for development only.
The forst dev HTTP contract (/invoke, /version, …) is documented in Dev server. Saving a .ft file regenerates the client so the editor stays in step. See Dev server § Generated client.
Failures
Every invoke failure has a stable_tag. Promise mode throws the tagged value. Effect mode exposes the same tags through its typed error channel:
Import invoke helpers from
@forst/errors in Promise mode or @forst/errors/effect in Effect mode (InvokeRejected, isInvokeFailure, …). Class names stay short. Built-in invoke, harness, and unknown-failure _tag strings use the @forst/errors/ prefix. Domain error tags are namespaced with your npm package name (for example @forst/tictactoe/CellTaken). Optional domain namespaces are also available from @forst/gen/$errors (for example errors.auth.NotFound).
Domain errors
When a Forst function raises a nominalerror X { ... }, the server includes structured errorValue on the invoke envelope (HTTP contract version "2"). The generated client decodes it into a tagged class with the same name as the Forst error type.
ForstUnknownFailure, and transport failures:
ForstUnknownFailure.
.safe()
Promise mode offers .safe(). Effect mode can move the typed failure into an Either value:
Per-call options
In Effect mode, fiber interruption cancels the HTTP request. Use
Effect.timeout and Effect.retry for timeouts and retry schedules.
Runtime support
The generated client targets Node.js 20.19+ server-side use (SSR, API routes, scripts). It assumesfetch, process.env, and optional AbortSignal.
For edge deployments, keep Forst behind a Node or Go invoke server and call it from the edge with
fetch and your own thin wrapper if needed.
Built-in HTTP server
Your compiled Go binary starts a local invoke server. On Unix it defaults to a Unix domain socket at.forst/invoke.sock. On Windows, or with FORST_INVOKE_TRANSPORT=tcp, it uses loopback TCP (default port 6321).
Enable with server.embedded in ftconfig.json:
forst build -o <dir> links a native invoke binary (requires server.embedded). It writes .forst/invoke.ready and a separate local token file. HMAC auth is on by default. Use the Forst client, sidecar, or CLI helpers for POST /invoke, because authenticated RPC needs a fresh challenge and HMAC proof. For a manual smoke test over TCP:
Environment variables
When the built-in server starts, Forst writes
.forst/invoke.ready with socketPath (Unix default) or url (TCP) for tooling auto-discovery.
Host mode (Remix and similar)
Incremental migration often runs three channels in one deployment:
Enable both
server.embedded and bridge.hostMode in ftconfig.json. Remix loaders call generated client functions over :6321; Forst main calls legacy JS over the nodert socket.
Full combined demo: examples/in/rfc/bridge-interop/remix-serve.
Bridgert setup is covered in Run in host mode.
Export rules
Only public functions with no unsatisfied Providers appear in the generated client. Functions that need wired dependencies belong in Forstmain startup, not in /invoke from Node.
Troubleshooting
Cannot find module '@forst/gen/...'
Cannot find module '@forst/gen/...'
Follow Installation § Generated TypeScript client. Fresh checkouts need
postinstall because .forst/client is gitignored and npm ci deletes the node_modules link.Connection refused / InvokeUnreachable
Connection refused / InvokeUnreachable
The binary or
forst dev is not listening. Check FORST_BASE_URL and the port in ftconfig.json (server.port, default 6321 for embedded).InvokeBaseUrlMissing in production
InvokeBaseUrlMissing in production
Set
FORST_BASE_URL. Production never auto-spawns forst dev.function not found in invoke response
function not found in invoke response
Check the
POST /invoke body: package must match the Forst package name, and function must match the exported name exactly. List available functions with GET /functions.Types do not match the server
Types do not match the server
Re-run
npx forst generate after changing .ft files. In development, regenerate-on-save keeps @forst/gen aligned. See Dev server.Compile error: function has unsatisfied providers
Compile error: function has unsatisfied providers
Wire providers in Go/
main before the server starts. Provider-dependent functions are excluded from the generated client surface.Caveats
Generated client invoke and the built-in HTTP server are experimental. Pin compiler versions in production.Check contractVersion
After upgrades, verify GET /version contractVersion matches what your client expects.
Spawn is local dev only
Auto-spawn offorst dev is for local development. Production must set FORST_BASE_URL and use the built-in HTTP server or another explicit connect target.
Exported functions only
Only capitalized function names appear in discovery and the invoke registry.Providers before expose
Wirewith blocks in Go main before the server starts. See Providers § Caveats.
Dev vs production URL
Executor profile (forst dev without embedded/host mode) often listens on 6320. Runtime profile (embedded or host mode) uses server.port (default 6321). A wrong FORST_BASE_URL looks like “works in dev, fails in prod”.
Related
Installation
@forst/cli, postinstall, and first generate.Generate a TypeScript client
Subpaths, types, and
@forst/gen layout.Testing
Stub calls with
withForstTestScope.Effect mode
Tagged errors and
generate.effect.Dev server
forst dev HTTP contract and regenerate on save.CLI reference
Full
generate configuration table.