Alchemy is an Infrastructure-as-Effects framework. It extends
Infrastructure-as-Code by combining your cloud resources and the
application logic that uses them into a single, type-safe program
powered by Effect.
Infrastructure-as-Code vs Infrastructure-as-Effects
Traditional IaC tools like Terraform, Pulumi, and CDK separate
infrastructure definitions from application code. You write your
infrastructure in one place and your business logic in another, then
wire them together with environment variables, ARNs, and config
files.
Alchemy takes a different approach: infrastructure and logic are
Effects in the same program.
// alchemy.run.ts — infrastructure and logic in one program
import*asAlchemyfrom"alchemy";
import*asCloudflarefrom"alchemy/Cloudflare";
import*asEffectfrom"effect/Effect";
importWorkerfrom"./src/worker.ts";
constBucket=Cloudflare.R2Bucket("Bucket");
exportdefaultAlchemy.Stack(
"MyApp",
{ providers:Cloudflare.providers() },
Effect.gen(function* () {
constbucket=yield*Bucket;
constworker=yield*Worker;
return { url:worker.url };
}),
);
// src/worker.ts — the Worker binds the Bucket directly
The Bucket resource, the Worker resource, and the runtime fetch
handler all live in the same codebase, composed with the same
yield* syntax. There’s no separate “infra” project — it’s one
program.
Alchemy uses TypeScript and Effect’s type system to catch mistakes at
compile time. If you forget to provide the right providers for your
resources, the compiler tells you:
@see ― effectDiscard for running an effect while providing no services
@category ― constructors
@since ― 2.0.0
empty,
Error ts(2322) ― Type 'Layer<never, never, never>' is not assignable to type 'Layer<NoInfer<Providers>, never, StackServices>'.
Type 'Providers' is not assignable to type 'never'.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
A Resource is any cloud entity managed by Alchemy — buckets,
databases, queues, workers, IAM roles, DNS records, and more. Each
resource is declared as an Effect and yield*-ed inside a Stack:
constbucket=yield*Cloudflare.R2Bucket("Bucket");
constdb=yield*Cloudflare.D1Database("DB");
constqueue=yield*AWS.SQS.Queue("Jobs");
Resources are just descriptions until they’re yielded. You can
declare them in separate files and import them from anywhere:
A Binding connects a resource to a Worker or Lambda. A single
bind() call hands your handler a typed client and — at deploy time
— wires up the permissions, environment variables, and platform
bindings that client needs:
bucket is the resource presented as a typed SDK. There’s no
env.BUCKET, no BUCKET_NAME lookup, and no hand-written IAM policy
— the binding is the client.
On AWS, bind() emits least-privilege IAM scoped to the exact
resource ARN. On Cloudflare, it attaches the native Worker binding
(R2, KV, D1, Durable Object). The runtime API is identical, so code
written against one consumes the other.
Bindings also enable circular references between resources —
Worker A can hold a typed client to Worker B and vice versa — which
plain props, a directed acyclic graph, can’t express.
See Bindings for event sources, sinks, and the
deploy-time mechanics.
A Provider teaches Alchemy how to manage a resource type. Behind
every resource is a provider implementing four lifecycle operations:
read — look up the resource’s live state in the cloud. It
tells the engine whether the resource exists and whether we own
it.
diff — compare the desired props against the previous ones
and decide whether the change is a no-op, an in-place update, or a
full replacement.
reconcile — make the cloud match the desired state. One flow
handles first-time creation, updates, and adoption: observe the
live state, create the resource if it’s missing, sync any drifted
fields, and return the output attributes.
delete — remove the resource. Idempotent, so “already gone”
counts as success.
The engine drives these in a plan → apply loop: read and
diff build the plan, then reconcile and delete apply it in
dependency order.
Providers are Effect Layers, wired into a Stack with
Cloudflare.providers() or AWS.providers():
Alchemy.Stack(
"App",
{ providers:Cloudflare.providers() } /* ... */,
);
The type system checks the wiring — using a Cloudflare resource
without Cloudflare.providers(), or providing the wrong cloud’s
providers, is a compile-time error. To support a new cloud or
third-party API, see Writing a Custom Resource
Provider.