Resource
A Resource represents a cloud entity managed by Alchemy — a bucket, database, queue, function, DNS record, or anything else that has a lifecycle of create, update, and delete.
Declaring a Resource
Section titled “Declaring a Resource”Resources are declared with a logical ID and optional input properties:
const bucket = yield * Cloudflare.R2Bucket("Bucket");const queue = yield * AWS.SQS.Queue("Jobs", { fifoQueue: true, });The logical ID ("Bucket", "Jobs") is stable across deploys. It
identifies this resource within the stack and is used to track state.
Input Properties and Output Attributes
Section titled “Input Properties and Output Attributes”Every resource has two sides:
- Input Properties — the desired configuration you pass in
(e.g.
fifoQueue: true) - Output Attributes — the values produced after creation
(e.g.
queueUrl,queueArn)
Output attributes are available as Output expressions on the
resource:
const bucket = yield * Cloudflare.R2Bucket("Bucket");bucket.bucketName; // Output<string>These are lazy references that resolve after the resource is created. You can pass them as inputs to other resources to express dependencies.
Resources are Effects
Section titled “Resources are Effects”A resource declaration like Cloudflare.R2Bucket("Bucket") is just
an Effect — it doesn’t execute anything on its own. It only runs when
you yield* it inside a Stack.
This means you can declare resources in separate files and import them freely:
export const Bucket = Cloudflare.R2Bucket("Bucket");
// src/worker.tsimport { Bucket } from "./bucket.ts";const bucket = yield * Cloudflare.R2Bucket.bind(Bucket);Importing a resource from multiple files is safe — it registers on the stack exactly once (keyed by its fully qualified name).
Physical Names
Section titled “Physical Names”Each resource gets a physical name generated from the stack name,
stage, and logical ID (e.g. myapp-dev-bucket-abc123). The suffix
is deterministic, derived from the resource’s instance ID.
Physical names ensure:
- Resources in different stages don’t collide
- Creates are idempotent (same inputs → same physical name)
- Resources can be recovered after state persistence failures
Replacement
Section titled “Replacement”When a property change requires replacing a resource (rather than updating in-place), Alchemy:
- Creates a new resource with a new instance ID
- Updates downstream resources to reference the new one
- Deletes the old resource
The resource’s provider determines which property changes trigger replacement vs in-place update.
Defining your own Resource type
Section titled “Defining your own Resource type”A resource is just a typed Effect. To support a new cloud or
third-party API, declare a Resource type with its input props and
output attributes — then implement the provider
as a Layer. Same engine plans, deploys, and destroys it.
// 1. Declare the type + constructor.export type StripeProduct = Resource< "Stripe.Product", { name: string; price: number }, // input props { productId: string; priceId: string } // output attrs>;export const StripeProduct = Resource<StripeProduct>("Stripe.Product");
// 3. Use it like any built-in resource.const Pro = yield* StripeProduct("Pro", { name: "Pro plan", price: 2900,});// ^? typed Pro.productId, Pro.priceId- Inputs & outputs are typed — Props you pass in, attributes the provider returns. Both fully typed, both checked at the call site.
- Compose with built-in providers — Merge your provider Layer with
Cloudflare.providers()orAWS.providers(). One stack, mixed clouds.
Outputs and the resource graph
Section titled “Outputs and the resource graph”A resource’s output attributes — bucketName, queueUrl,
domainName — aren’t strings. They’re Output<T> values that resolve
after the resource is created. Pass an Output as input to another
resource and you’ve drawn an edge in the dependency graph: alchemy
deploys in topological order, resolving Outputs as they become
available.
Output.map— Transform an Output without forcing it. The mapping runs after the upstream resource resolves.Output.interpolate— Tagged template that splices Outputs into strings. Typed, lazy, and tracks the dependency.- Topological deploy — The graph drives the order. Independent resources deploy in parallel. Cycles get a two-phase plan (see below).
Circular references
Section titled “Circular references”Real systems have cycles. Two Workers that call each other. A Lambda that invokes another Lambda. Tables that reference each other. Most IaC engines reject these — alchemy resolves them in two passes: create the resources, then wire the bindings, using deferred Outputs so types stay sound.
// Worker A binds Worker B. Worker B binds Worker A.// Most IaC engines refuse to plan this. Alchemy resolves// it in two passes: create the resources, then wire bindings.
export default class A extends Cloudflare.Worker<A>()( "A", Effect.gen(function* () { const b = yield* B; return { fetch: (req) => b.fetch(req) }; }),) {}
export default class B extends Cloudflare.Worker<B>()( "B", Effect.gen(function* () { const a = yield* A; return { fetch: (req) => a.fetch(req) }; }),) {}