Skip to content

Getting Started

This guide will deploy a Cloudflare Worker and bind resources to it with Alchemy.

TIP

Read What is Alchemy to get an overview of Alchemy and how it's different than traditional IaC

Prerequisites

Initialize Project

sh
mkdir my-alchemy-app
cd my-alchemy-app
bun init -y
sh
mkdir my-alchemy-app
cd my-alchemy-app
npm init -y
npm install -D typescript @types/node tsx
sh
mkdir my-alchemy-app
cd my-alchemy-app
pnpm init
pnpm add -D typescript @types/node tsx
sh
mkdir my-alchemy-app
cd my-alchemy-app
yarn init -y
yarn add -D typescript @types/node tsx

Install Dependencies

sh
bun add alchemy
bun add -g wrangler
sh
npm install alchemy
npm install -g wrangler
sh
pnpm add alchemy
pnpm add -g wrangler
sh
yarn add alchemy
yarn global add wrangler

Login to Cloudflare

sh
wrangler login

This will open your browser to authenticate with your Cloudflare account.

TIP

Alchemy automatically uses your wrangler OAuth token. See the Cloudflare Auth guide for alternative authentication methods.

Create alchemy.run.ts

sh
my-project/
└── alchemy.run.ts

In alchemy.run.ts, import alchemy and initialize the application:

typescript
import alchemy from "alchemy";
import { Worker } from "alchemy/cloudflare";

const app = await alchemy("my-first-app");

Deploy a Cloudflare Worker:

typescript
const worker = await Worker("hello-worker", {
  // reference the worker script (we will create that next)
  entrypoint: "./src/worker.ts",
});

Print out its URL:

ts
// log out its URL
console.log(`Worker deployed at: ${worker.url}`);

Finally, make sure to call app.finalize() at the very end to delete resources that are no longer needed:

typescript
// Finalize the app to apply changes
await app.finalize();

Create Worker Entrypoint

The Worker resource we created before will try to bundle entrypoint: "./src/worker.ts".

sh
my-project/
├── src/
   └── worker.ts
└── alchemy.run.ts

Let's create this script and implement our HTTP server:

typescript
export default {
  async fetch(request: Request): Promise<Response> {
    return Response.json({
      message: "Hello from Alchemy!",
      timestamp: new Date().toISOString(),
    });
  },
};

Deploy the Worker

Simply run your alchemy.run.ts script to deploy this Worker to Cloudflare:

sh
bun ./alchemy.run.ts
sh
npx tsx ./alchemy.run.ts
sh
pnpm tsx ./alchemy.run.ts
sh
yarn tsx ./alchemy.run.ts

You will see output similar to:

Create:  "my-first-app/dev/hello-worker"
Created: "my-first-app/dev/hello-worker"
Worker deployed at: https://hello-worker.your-subdomain.workers.dev

Visit the URL to see your worker in action!

TIP

If you're familiar with other IaC tools, this should feel similar to terraform apply, pulumi up, cdk deploy or sst deploy

Add Bindings

Now let's add some infrastructure to our worker. We'll add a KV namespace for storage.

First, update your alchemy.run.ts to import the KVNamespace Resource

typescript
import { KVNamespace } from "alchemy/cloudflare";

Then, create a new KVNamespace:

ts
const kv = await KVNamespace("my-app-storage");

And bind it to our Worker resource we created before:

ts
const worker = await Worker("hello-worker", {
  entrypoint: "./src/worker.ts",
  bindings: {
    // bind the KV Namespace to the Worker
    KV: kv,
  },
});

NOTE

Learn more in the Cloudflare Bindings documentation.

Finally, update your ./src/worker.ts to use these bindings:

typescript
// type-only import the worker from your `alchemy.run.ts`
import type { worker } from "../alchemy.run.ts";

export default {
  // use `typeof worker.Env` to infer the environment types from the bindings
  async fetch(request: Request, env: typeof worker.Env): Promise<Response> {
    // Store and retrieve data with type safety
    await env.KV.put("last-visit", new Date().toISOString());
    const lastVisit = await env.KV.get("last-visit");

    return Response.json({
      message: "Hello from Alchemy!",
      timestamp: new Date().toISOString(),
      lastVisit: lastVisit,
      apiKey: env.API_KEY, // TypeScript knows this is a string
    });
  },
};

NOTE

The typeof worker.Env type infers types directly from your infrastructure definition in alchemy.run.ts. No code generation required!

TIP

You can also infer types for import { env } from "cloudflare:workers", see the type-safe bindings documentation to learn more.

Understanding State

After running your app, Alchemy creates a .alchemy directory to store state:

sh
.alchemy/
  my-first-app/         # app
    dev/                # stage
      hello-worker.json # resource state

State files help Alchemy determine whether to create, update, delete, or skip resources on subsequent runs.

TIP

For production environments or CI/CD, you should use a persistent state store like DOStateStore (preferred) or R2RestStateStore.

Local Development

Alchemy currently relies on wrangler CLI for local development.

You can generate a wrangler.json from your Worker using the WranglerJson Resource:

typescript
// Add this import
import { WranglerJson } from "alchemy/cloudflare";

// ... after creating the worker with bindings ...

// Generate wrangler.json for local development
await WranglerJson("wrangler.json", {
  worker,
});

Deploy again to generate the wrangler.json with your bindings:

sh
bun ./alchemy.run.ts
sh
npx tsx ./alchemy.run.ts
sh
pnpm tsx ./alchemy.run.ts
sh
yarn tsx ./alchemy.run.ts

Now you can your worker locally using wrangler dev:

sh
wrangler dev

Visit http://localhost:8787 to test your worker locally with all your bindings!

Tear Down

When you're done, you can destroy all resources:

sh
bun ./alchemy.run.ts --destroy
sh
npx tsx ./alchemy.run.ts --destroy
sh
pnpm tsx ./alchemy.run.ts --destroy
sh
yarn tsx ./alchemy.run.ts --destroy

Output:

Delete:  "my-first-app/dev/hello-worker"
Deleted: "my-first-app/dev/hello-worker"

Your worker is now removed from Cloudflare.

Next Steps

You've successfully deployed your first Cloudflare Worker with Alchemy! Here are some next steps: