1---
2title: What is Alchemy?
3description: Alchemy is Infrastructure as Code built in pure Effect, with Infrastructure as Effects on top. Declare your cloud resources and the code that runs on them in one type-safe TypeScript program, and deploy it with one command.
4prev:
5 link: /getting-started
6 label: Getting started
7next:
8 link: /infrastructure-as-effects/runtime
9 label: Runtime
10---
11
12import DeployTerminal from "../../components/marketing-islands/DeployTerminal.tsx";
13import ProviderIcons from "../../components/ProviderIcons.astro";
14
15Alchemy is **Infrastructure as Code** built in pure
16[Effect](https://effect.website), with **Infrastructure as Effects**
17on top. Infrastructure as Code declares, diffs, and deploys cloud
18resources the way Terraform or Pulumi does. Infrastructure as Effects
19lets the code that runs on those resources live in the same program,
20as typed Effects and Layers.
21
22Here is the whole thing in one file. An R2 Bucket, and a Worker that
23serves files from it:
24
25```typescript
26// src/api.ts
27import * as Cloudflare from "alchemy/Cloudflare";
28import * as Effect from "effect/Effect";
29import * as HttpServerResponse from "effect/http/HttpServerResponse";
30
31export const Uploads = Cloudflare.R2.Bucket("Uploads");
32
33export default Cloudflare.Worker(
34 "Api",
35 { main: import.meta.url },
36 Effect.gen(function* () {
37 const bucket = yield* Cloudflare.R2.ReadWriteBucket(Uploads);
38
39 return {
40 fetch: Effect.gen(function* () {
41 const obj = yield* bucket.get("hello.txt");
42 return obj
43 ? HttpServerResponse.text(yield* obj.text())
44 : HttpServerResponse.text("Not found", { status: 404 });
45 }),
46 };
47 }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)),
48);
49```
50
51That one file is a complete application. The Bucket is declared next
52to the Worker that uses it, and `bucket` is a typed client the handler
53closes over. Let's start with Infrastructure as Code, then look at what
54Infrastructure as Effects adds on top.
55
56## Infrastructure as Code
57
58The core of Alchemy is Infrastructure as Code, in the same family as
59Terraform, Pulumi, CloudFormation, and the CDK.
60
61### Stack
62
63A **Stack** is the unit you deploy, an Effect that yields resources
64and returns the outputs you want printed:
65
66```typescript
67// alchemy.run.ts
68import * as Alchemy from "alchemy";
69import * as Cloudflare from "alchemy/Cloudflare";
70import * as Effect from "effect/Effect";
71import Api from "./src/api.ts";
72
73export default Alchemy.Stack(
74 "MyApp",
75 { providers: Cloudflare.providers(), state: Cloudflare.state() },
76 Effect.gen(function* () {
77 const api = yield* Api;
78 return { url: api.url };
79 }),
80);
81```
82
83Deploy it:
84
85```sh
86bun alchemy deploy
87```
88
89Alchemy creates the Bucket, bundles the Worker, wires the binding
90between them, and prints the URL. Every deploy targets a stage, an isolated environment with its own
91copy of every resource, so `dev` and `prod` never touch each other:
92
93```sh
94alchemy deploy # dev_$USER by default
95alchemy deploy --stage prod
96```
97
98Alchemy reads the current state, diffs it against your program, shows
99the plan, and applies it in dependency order. Here is that loop for a
100Stack with a Bucket, a KV Namespace, and a Worker bound to both:
101
102<DeployTerminal client:visible />
103
104**State** is where the result persists between runs, so the next
105deploy only touches what changed — the `state` option above picks
106where it's stored.
107
108### Resource
109
110A **Resource** is a cloud entity in a Stack managed by Alchemy: a
111bucket, a database, a queue, a Worker, a DNS record. Yield it in the
112Stack to add it to the plan:
113
114```typescript
115const bucket = yield* Cloudflare.R2.Bucket("Uploads");
116// bucket.bucketName is an Output
117```
118
119Each Resource has a logical id, `"Uploads"` here, that Alchemy uses to
120track it across deploys. Its outputs are typed values you can pass
121into other Resources. Here the physical bucket name Alchemy generated
122becomes an environment variable on a Worker:
123
124```typescript
125const bucket = yield* Cloudflare.R2.Bucket("Uploads");
126
127yield* Cloudflare.Worker("Api", {
128 main: "./src/api.ts",
129 env: { BUCKET_NAME: bucket.bucketName },
130});
131```
132
133### Provider
134
135A **Provider** teaches Alchemy how to read, diff, create, update, and
136delete one resource type. Each cloud ships its providers as an Effect
137Layer, and a Stack takes as many as it needs:
138
139```typescript
140providers: Layer.mergeAll(Cloudflare.providers(), AWS.providers()),
141```
142
143The type system checks the wiring. Yield an AWS resource in a Stack
144that only provides `Cloudflare.providers()` and the program does not
145compile. [Providers](/infrastructure-as-code/provider) covers the
146built-in ones, and
147[Custom Provider](/infrastructure-as-code/custom-provider) shows how
148to write your own.
149
150## A superset of Infrastructure as Code
151
152With only Infrastructure as Code, Alchemy looks like and functions like any other
153IaC tool. One file
154declares the Bucket and the Worker, passing the Bucket on the Worker's
155`env`. Another file is the handler, reaching for the Bucket through
156that env:
157
158```typescript
159// alchemy.run.ts
160import * as Cloudflare from "alchemy/Cloudflare";
161
162export const Uploads = Cloudflare.R2.Bucket("Uploads");
163
164export const Api = Cloudflare.Worker("Api", {
165 main: "./src/api.ts",
166 env: { Uploads },
167});
168
169export type ApiEnv = Cloudflare.InferEnv<typeof Api>;
170```
171
172```typescript
173// src/api.ts
174import type { ApiEnv } from "../alchemy.run.ts";
175
176export default {
177 async fetch(request: Request, env: ApiEnv) {
178 const obj = await env.Uploads.get("hello.txt");
179 return obj
180 ? new Response(await obj.text())
181 : new Response("Not found", { status: 404 });
182 },
183};
184```
185
186This works, and it is fully supported. `InferEnv` even types the env
187from the declaration, which is more than most IaC tools give you. But
188the two files are still held together by the name `Uploads`, and the
189handler knows nothing about how the Bucket got there. Infrastructure
190as Effects is what turns those two files into the one at the top of
191this page.
192
193:::tip
194Alchemy also supports circular references in the
195dependency graph, which most IaC tools reject. See
196[Circular Bindings](/infrastructure-as-effects/circular-bindings).
197:::
198
199## Infrastructure as Effects
200
201Infrastructure as Effects adds two new concepts to Infrastructure as
202Code: Runtimes and Bindings.
203
204### Runtime
205
206A **Runtime** is a Resource that carries the code it runs: a Worker,
207Lambda Function, Container, or Server. That code is always written
208the same way, as an **Effectful Constructor**. Bind what you need,
209then return what you expose:
210
211```typescript
212Effect.gen(function* () {
213 // bind what you need
214 const bucket = yield* Cloudflare.R2.ReadWriteBucket(Uploads);
215
216 // return what you expose
217 return { fetch: /* ... */ };
218});
219```
220
221The outer Effect runs both at deploy time and at cold start; what it
222returns runs per request. A Worker returns `fetch`, a Durable Object
223returns its RPC methods, a Workflow returns its run function, and
224every Runtime in Alchemy is a variation on that one shape.
225
226### Binding
227
228A **Binding** connects a Resource to the Runtime that uses it:
229
230```typescript
231const bucket = yield* Cloudflare.R2.ReadWriteBucket(Uploads);
232// bucket.get / bucket.put / bucket.list, typed end to end
233```
234
235It hands back a typed client and generates whatever that client needs
236to work. On Cloudflare that is a native Worker binding. On AWS it is
237an IAM statement scoped to one resource, plus the resource's name in
238the Function's environment:
239
240```typescript
241const getItem = yield* AWS.DynamoDB.GetItem(Jobs);
242// → { Action: ["dynamodb:GetItem"], Resource: [Jobs.tableArn] }
243// → Jobs_tableName=Jobs-a1b2c3
244```
245
246There is no `env.Uploads` to reach for and no hand-written policy. The
247binding is the SDK.
248
249### Layers
250
251Every Binding is a contract paired with a **Layer** that fulfills it:
252the handler is written against the contract, and the Layer decides
253how — swap the Layer and the handler doesn't change:
254
255```diff lang="typescript"
256 Effect.gen(function* () {
257 const bucket = yield* Cloudflare.R2.ReadWriteBucket(Uploads);
258 // ...
259-}).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)); // native Worker binding
260+}).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketHttp)); // HTTP with a scoped token
261```
262
263The same split works for services of your own. A Layer can own
264Resources and Bindings outright:
265
266```typescript
267export const JobServiceKV = Layer.effect(
268 JobService,
269 Effect.gen(function* () {
270 const Jobs = yield* Cloudflare.KV.Namespace("Jobs"); // a Resource, owned by the Layer
271 const kv = yield* Cloudflare.KV.ReadWriteNamespace(Jobs); // and its Binding
272 return { getJob: (id: string) => kv.get<Job>(id, "json") };
273 }),
274);
275```
276
277Business logic is implemented against the `JobService` contract. The
278Layer decides the infrastructure behind it, so swapping KV for
279DynamoDB is one line:
280
281```diff lang="typescript"
282 Effect.gen(function* () {
283 const jobs = yield* JobService;
284 // ...
285-}).pipe(Effect.provide(JobServiceKV)); // a KV Namespace, created and bound
286+}).pipe(Effect.provide(JobServiceDynamo)); // now a DynamoDB Table instead
287```
288
289The Infrastructure as Effects section goes deeper from here:
290[Event Sources](/infrastructure-as-effects/event-sources) and
291[Sinks](/infrastructure-as-effects/sinks) turn resources into Effect
292`Stream`s, [Phases](/infrastructure-as-effects/phases) pins down
293exactly what runs when,
294[Circular Bindings](/infrastructure-as-effects/circular-bindings)
295lets two Workers call each other, and
296[Telemetry](/infrastructure-as-effects/telemetry) exports every
297request's traces and logs.
298
299## Where next
300
301<div class="next-steps">
302
303<a href="/infrastructure-as-effects/runtime" class="next-card next-card--recommended">
304 <strong>Runtime</strong>
305 <span>The Effectful Constructor in depth: the fetch and RPC interface, and the three ways to declare a Runtime.</span>
306</a>
307
308<a href="/cloudflare" class="next-card">
309 <strong>Cloudflare</strong>
310 <span>Workers, Durable Objects, R2, KV, D1, Queues, Workflows, and Containers, with a tutorial and a guide for each.</span>
311</a>
312
313<a href="/aws" class="next-card">
314 <strong>AWS</strong>
315 <span>Lambda, S3, SQS, DynamoDB, EC2, ECS, and EKS, with a tutorial and a guide for each.</span>
316</a>
317
318<a href="/providers" class="next-card">
319 <strong>More providers</strong>
320 <ProviderIcons />
321</a>
322
323</div>