Skip to content

Part 4: Local Dev

In Part 3 you wrote integration tests against your deployed stack. But deploying to Cloudflare on every code change is slow. In this part you’ll use alchemy dev to run everything locally with hot reloading.

Terminal window
alchemy dev

That’s it. Alchemy deploys your stack to the cloud and proxies requests to Workers running locally in workerd. It watches for file changes and hot reloads your Worker automatically.

Your Worker is available at http://localhost:1337 by default:

Terminal window
curl http://localhost:1337/hello.txt

alchemy dev does three things:

  1. Deploys to the cloud — resources like R2 Buckets are created on Cloudflare, while Workers run locally in workerd
  2. Watches for changes — when you edit src/worker.ts or any file it imports, the Worker is rebuilt and reloaded instantly
  3. Preserves local state — data in your local R2 Bucket persists across restarts

The same alchemy.run.ts is used for both dev and deploy — no separate dev config needed.

Set a custom port for your Worker:

src/worker.ts
export default Cloudflare.Worker(
"Worker",
{
main: import.meta.path,
dev: {
port: 3000,
},
},
// ...
);

You don’t need to run alchemy dev in a separate terminal to test against locally-emulated resources. The test harness has its own dev flag that flips every Worker over to workerd inside the test process — same wiring as alchemy dev, no extra terminal.

test/integ.test.ts
const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
providers: Cloudflare.providers(),
state: Cloudflare.state(),
dev: true,
});

beforeAll(deploy(Stack)) now boots the Workers locally; HTTP assertions hit http://localhost:1337 and roundtrip into your code with full stack traces.

Hardcoding dev: true would force CI runs to local emulation too. Read it from the environment instead so the same test file runs against real cloud resources in CI and locally-emulated ones on your laptop.

const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
providers: Cloudflare.providers(),
state: Cloudflare.state(),
dev: true,
});

Drop the explicit flag and the harness falls back to the ALCHEMY_DEV env var — set ALCHEMY_DEV=1 in your shell to flip to local mode, leave it unset in CI.

Terminal window
ALCHEMY_DEV=1 bun test test/integ.test.ts

You now have:

  • Local development with alchemy dev
  • Hot reloading on file changes
  • Local emulation of Cloudflare resources

In Part 5, you’ll set up GitHub Actions for automated deployments and PR preview environments.