Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions src/content/docs/workers/platform/infrastructure-as-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,190 @@ resource "cloudflare_workers_deployment" "my_worker_deployment" {

Notice how you don't have to manage all of these resources in Terraform. For example, you could just the `cloudflare_worker` resource and seamlessly use Wrangler or your own deployment tools for Versions or Deployments.

## Alchemy

[Alchemy](https://alchemy.run/) is a TypeScript-native Infrastructure as Code tool that lets you define, deploy, and version Cloudflare resources alongside your application code.
Unlike traditional IaC tools, Alchemy integrates directly with your codebase and provides full TypeScript type safety without external schemas or code generation.

### Installation

```bash
bun add alchemy
```

### Minimal setup

Create an `alchemy.run.ts` file in your project root:

```ts
import "alchemy/cloudflare";
import alchemy from "alchemy";
import { Worker, Queue, KVNamespace } from "alchemy/cloudflare";

const app = await alchemy("my-app", {
stage: process.env.USER ?? "dev",
phase: process.argv.includes("--destroy") ? "destroy" : "up",
});

const cache = await KVNamespace("cache", {
title: "cache-store"
});

const jobs = await Queue("jobs", {
name: "background-jobs"
});

const api = await Worker("api", {
name: "api-worker",
entrypoint: "./src/api.ts",
compatibilityDate: "2025-09-13",
bindings: {
CACHE: cache,
JOBS: jobs,
API_KEY: alchemy.secret("your-api-key")
}
});

console.log(`Worker URL: ${api.url}`);

await app.finalize();
```

The Worker example at `./src/api.ts`:
```ts
import type { api } from "../alchemy.run.ts";

export default {
async fetch(request: Request, env: typeof api.Env) {
// Full type safety for all bindings
await env.CACHE.put("key", "value");
await env.JOBS.send({ task: "process" });

return new Response("OK");
}
};
```

Deploy your infrastructure:

```bash
bun alchemy.run.ts
```

Destroy resources when needed:

```bash
bun alchemy.run.ts --destroy
```

### Durable Objects

Define a Durable Object namespace and bind it to your Worker. Alchemy automatically generates migrations when you specify a `className`, eliminating the need to manually manage Durable Object migrations:

```ts
import { Worker, DurableObjectNamespace } from "alchemy/cloudflare";

const counter = DurableObjectNamespace("counter", {
className: "Counter",
sqlite: true
});

const worker = await Worker("api", {
entrypoint: "./src/worker.ts",
bindings: { COUNTER: counter }
});
```

Implement the Durable Object in `./src/worker.ts`:

```ts
import { DurableObject } from "cloudflare:workers";
import type { worker } from "../alchemy.run.ts";

export class Counter extends DurableObject {
async increment(): Promise<number> {
let count = (await this.ctx.storage.get("count")) || 0;
count++;
await this.ctx.storage.put("count", count);
return count;
}

async fetch(request: Request): Promise<Response> {
const count = await this.increment();
return Response.json({ count });
}
}

export default {
async fetch(request: Request, env: typeof worker.Env) {
const id = env.COUNTER.idFromName("global-counter");
const obj = env.COUNTER.get(id);
return obj.fetch(request);
}
};
```

### Queue consumer example

Background jobs with retries and dead letter queues; the boring way that scales:

```ts
import { Worker, Queue } from "alchemy/cloudflare";

const taskQueue = await Queue("tasks", {
name: "task-processing"
});

const dlq = await Queue("failed", {
name: "failed-tasks"
});

const processor = await Worker("processor", {
entrypoint: "./src/processor.ts",
bindings: { TASKS: taskQueue },
eventSources: [{
queue: taskQueue,
settings: {
batchSize: 15,
maxConcurrency: 3,
maxRetries: 5,
maxWaitTimeMs: 2500,
deadLetterQueue: dlq
}
}]
});
```

The processor at `./src/processor.ts`:

```ts
import type { MessageBatch } from "@cloudflare/workers-types";

export default {
async queue(batch: MessageBatch, env: Env) {
for (const message of batch.messages) {
try {
await processTask(message.body);
message.ack();
} catch (error) {
message.retry({ delaySeconds: 30 });
}
}
}
};
```

### Why use Alchemy

- **Type-safe everything**: Full autocompletion for Cloudflare resources without code generation or external schemas
- **Single source of truth**: Infrastructure and app code evolve together—atomic changes, easier rollbacks
- **Environment inference**: Worker types are automatically inferred from your infrastructure definition
- **Local-first**: Test infrastructure changes locally with the same fast feedback as your app code

### Important considerations

Refer to the [Alchemy documentation](https://alchemy.run/) for best practices on credentials, state management, bundling, and deployment workflows

## Cloudflare API Libraries

This example uses the [cloudflare-typescript](https://github.com/cloudflare/cloudflare-typescript) SDK which provides convenient access to the Cloudflare REST API from server-side JavaScript or TypeScript.
Expand Down