Runtime
An Effect is just a description of a computation — building one runs nothing.
Nothing happens until you hand that description to a runtime, which spawns a
fiber to actually carry it out. This is the same principle as a lazy () => Promise: you compose effects freely, then execute the whole program in one
place.
The golden rule is to run effects at the edge of your application. Keep the
vast majority of your logic as composed effects, and call a run* function (or
a platform runMain) once, as close to the “outside world” as possible — your
main.ts, an HTTP handler, a test. Pushing execution to the edge keeps your
code composable, testable, and free of half-run side effects scattered through
the middle of your program.
import { Effect } from "effect"
// A description. Nothing has happened yet.const program = Effect.gen(function*() { yield* Effect.log("Hello from a fiber") return 42})
// Execution happens here, once, at the edge.Effect.runPromise(program).then((n) => console.log(n))// Output:// ... level=INFO message="Hello from a fiber"// 42What’s in this section
Section titled “What’s in this section”- Running effects — the
run*family (runSync,runPromise,runFork, and theirExitvariants), and how to choose between them. - Running as an entrypoint —
NodeRuntime.runMain/BunRuntime.runMain, the production-grade way to make an Effect your process root, with signal handling and graceful shutdown. - Launching applications —
Layer.launch, for long-running apps (servers, workers) expressed entirely as layers. - Managed runtime — build a single
runtime from your application
Layerand run many effects against it, so services and resources are shared instead of constructed per run. The way to bridge Effect into a non-Effect host (a web framework, a UI event loop).
Choosing how to run
Section titled “Choosing how to run”| You want… | Use |
|---|---|
| A process entrypoint (CLI, server, worker) | NodeRuntime.runMain / BunRuntime.runMain |
| A long-running app built from layers | Layer.launch + runMain |
| To share services/resources across runs | ManagedRuntime.make(layer) |
A Promise for interop | Effect.runPromise |
| A background fiber to observe or interrupt | Effect.runFork |
| An immediate, purely synchronous result | Effect.runSync |
For most applications you will reach for runMain rather than calling the
low-level run* functions directly — it adds error reporting and graceful
shutdown on top of runFork. The Effect.run* functions are the right tool
when embedding Effect inside an existing host (a React event handler, an Express
route, an existing async function).
When that host fires events repeatedly and your effects need shared services
(database pools, config, caches), don’t build a fresh layer on every event.
Build a ManagedRuntime once from your
application Layer and call runtime.runPromise(effect) per event — the layer
is constructed a single time and torn down with runtime.dispose() on shutdown.
Effects must have their requirements satisfied before they can run. A
run*/runMain function only accepts an Effect<A, E, never> — the R
channel must be empty. You eliminate requirements by providing
services and layers before reaching the edge.
For writing your own process entrypoint or platform adapter, the core Runtime
module exposes the building blocks the runMain adapters are made from
(makeRunMain, Teardown, defaultTeardown, and the errorExitCode /
errorReported markers). Those are documented alongside the practical recipe on
the Running as an entrypoint page.
Carrying services across many runs
Section titled “Carrying services across many runs”For repeated execution against a shared set of services, a one-shot run* is
the wrong shape — it builds and discards everything each call. Build a
ManagedRuntime from your application
Layer once, then run effects against it as many times as you like. The layer
(and its scoped resources) is constructed lazily on first use and released when
you dispose.
import { Effect, Layer, ManagedRuntime } from "effect"
// Your application's services, expressed as a Layer.const AppLayer = Layer.empty
const runtime = ManagedRuntime.make(AppLayer)
// Run many effects; services are shared, not rebuilt each time.await runtime.runPromise(Effect.log("request 1"))await runtime.runPromise(Effect.log("request 2"))
// Release layer resources on shutdown.await runtime.dispose()A ManagedRuntime exposes runPromise, runPromiseExit, runFork,
runSync, runSyncExit, plus dispose / disposeEffect. See
Managed runtime for the full reference
and framework-integration examples.