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.
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 |
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).
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.