Phases
Every alchemy program operates in two distinct phases:
- Plantime — the deploy. Runs
alchemy deploy,plan, ordev. Produces the resource graph, runs providers, persists state. - Runtime — the deployed handler. Runs inside a Worker or Lambda Function whenever a request comes in.
Platform resources express both in a single program by returning an Effect from inside an Effect.
Init vs Exec
Section titled “Init vs Exec”Cloudflare.Worker( "Worker", { main: import.meta.path }, Effect.gen(function* () { // ─── Init phase ─── const bucket = yield* Cloudflare.R2Bucket.bind(Bucket);
return { // ─── Exec phase ─── fetch: Effect.gen(function* () { const obj = yield* bucket.get("key"); return HttpServerResponse.text(yield* obj.text()); }), }; }),);| Phase | Code | When it runs |
|---|---|---|
| Init | outer | At plantime and at cold start |
| Exec | inner | Only at runtime, per request |
The bucket value is established once during init and captured by
exec. Init runs at most once per cold start; exec runs per request
with everything already wired up.
What runs when
Section titled “What runs when”- At plantime, init runs to discover bindings — alchemy needs to know which resources the handler will use so it can wire permissions, env vars, and references.
- At runtime cold start, init runs again — this time inside the
deployed Worker, where the same
bind()calls return live SDK clients backed by the deployed resource. - Exec only runs at runtime. It never executes at plantime, so you can put real per-request work there without affecting deploy speed.
ALCHEMY_PHASE
Section titled “ALCHEMY_PHASE”The current phase is exposed as the ALCHEMY_PHASE environment
variable / config key:
| Value | Context |
|---|---|
plan | Default. Running alchemy deploy or alchemy plan. |
dev | Running alchemy dev (local development with hot reload). |
runtime | Running inside a deployed Worker or Lambda Function. |
Most user code never reads this directly — but providers and bindings use it internally to behave differently across phases.
Binding.Service vs Binding.Policy
Section titled “Binding.Service vs Binding.Policy”A binding has two layers under the hood — and which one runs depends on the phase:
| Layer | Phase | Job |
|---|---|---|
Binding.Service | Runtime | The lightweight typed SDK that ships with the bundle. |
Binding.Policy | Plan | The deploy-time logic that emits IAM / env / config. |
At plantime the Policy layer is provided, so calling bind()
records what the function will need. At runtime the Policy layer is
absent — bind() returns just the Service wrapper. The runtime
bundle stays small because none of the planning code is included.
This is also how alchemy can let you write bucket.get(...) inside
a Worker without bundling AWS / Cloudflare provisioning code:
provisioning lives in Policy, not Service.
Why this matters
Section titled “Why this matters”The init/exec split lets you write code that:
- Resolves infrastructure references at deploy time — bindings know which bucket ARN, queue URL, etc. to inject.
- Initializes SDK clients once at cold start — not on every request.
- Handles requests with a pre-configured context — the
bucketvariable in exec already knows which resource to talk to.
Effect.gen(function* () { // Init: runs once per cold start const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); const kv = yield* Cloudflare.KVNamespace.bind(KV);
return { // Exec: runs per request fetch: Effect.gen(function* () { const obj = yield* bucket.get("key"); // ... }), };});Continue to Binding for the full mechanics of
how .bind() wires resources into your handler.