Reach for the better primitives

Snap together cloud resources and let Alchemy do the wiring so you can focus on your product.

The full picture

Alchemy can deploy, test, and destroy your app. That's everything you need to have a full CI experience with branch previews and live test on real infratructure. Oh and the graphic below is how long it actually takes; Alchemy does all of that in ~25 seconds.

See the example app and benchmark on GitHub

Add image upload… #147
github.com/acme/my-app/pull/147
Add image upload to /photos #147
Openfeature/photo-upload→main
youopened this pull request · 3 commits +142 −8
PR / preview (pull_request)Queued
Actions · acme/my-appPULL_REQUEST
previewpull_request · pr-147
on: pull_request (opened) · STAGE=pr-147
Checkout
Setup runtime
Install dependencies
alchemy deploy --stage pr-147

Run your entire stack locally

As apps grow you have more and more services; each backend service needs to be started in a local emulator or proxy a live service, each client needs its own dev server, and you have to set up HMR. alchemy dev manages all of that for you, 1 command, no extra configuration, and all your services just start.

alchemy.run.ts
import Api from "./src/api.ts";

export default Alchemy.Stack(
  "my-app",
  { providers: Cloudflare.providers() },
  Effect.gen(function* () {
    // backend: a Cloudflare Worker, run locally in workerd
    const api = yield* Api;
    // frontend: a Vite app that calls it
    const web = yield* Cloudflare.Website.Vite("Web", {
      env: { VITE_API_URL: api.url },
    });
    return { web: web.url, api: api.url };
  }),
);
~/my-appDEV

Run tests against real infrastructre

alchemy test deploys a temporary stack, runs your tests against it, then tears it down. This way tests hit a real environment instead of mocks or local emulation, and agent loops stay accurate. In CI, tests can inherit the preview deployment stack so deploy doesn't run twice.

test/api.test.ts
// each suite deploys its own isolated stack
const stack = beforeAll(deploy(Stack));   // real R2, real DynamoDB
afterAll(destroy(Stack));                 // torn down at the end

test("PUT + GET round-trips through R2", Effect.gen(function* () {
  const { url } = yield* stack;
  const res = yield* HttpClient.get(`${url}/object/hello.txt`);
  expect(yield* res.text).toBe("hi!");
}));
CI · pr-1729TEST
$

Dry run before you deploy

plan does a dry-run and show what will change.deploy updates your infrastructure to match the desired state. destroy tears it all down (with safe guards to prevent accidental data deletion). Simple enough for agents, easy enough you'll choose to use it too

src/api.ts
export const Photos = Cloudflare.R2.Bucket("Photos");
export const Sessions = Cloudflare.KV.Namespace("Sessions");

const Bindings = Layer.mergeAll(
  Cloudflare.R2.ReadWriteBucketBinding,
  Cloudflare.KV.ReadWriteNamespaceBinding,
);

export default Cloudflare.Worker(
  "Api",
  { main: import.meta.url },
  Effect.gen(function* () {
    const photos = yield* Cloudflare.R2.ReadWriteBucket(Photos);
    const kv = yield* Cloudflare.KV.ReadWriteNamespace(Sessions);
    return {
      fetch: Effect.gen(function* () {
        const { objects } = yield* photos.list();
        const user = yield* kv.get("user");
        return yield* HttpServerResponse.json({ user, objects });
      }),
    };
  }).pipe(Effect.provide(Bindings)),
);
~/my-app
$

Wired for observability

Alchemy has logs, spans, and metrics set up by default. The exporter is an Effect Layer so changing providers is a 1 line swap, whether its Axiom, Datadog, or any other OTLP endpoint.
Dashboards and alarms are resources themselve so you can define them right next to the resources they are responsible for.

src/Api.ts
const requestsTotal = Metric.counter("requests_total");

// any OTLP endpoint: Axiom, Datadog, Honeycomb, a collector…
const Otel = Alchemy.Telemetry.layerOtlp({
  url: "https://otlp.example.com",
});

export default Cloudflare.Worker(
  "Api",
  { main: import.meta.url },
  Effect.gen(function* () {
    yield* Effect.logInfo("request received");
    yield* Metric.increment(requestsTotal);
    return { fetch: handler };
  }).pipe(Effect.provide(Otel)),
);
alchemy.run.ts
export const Dashboard = AWS.CloudWatch.Dashboard("ApiHealth", {
  widgets: [
    Widget.line({ title: "p99 latency", metric: api.metrics.p99 }),
    Widget.line({ title: "requests/s", metric: api.metrics.rps }),
    Widget.number({ title: "5xx", metric: api.metrics.errorRate }),
  ],
});

export const P99Alarm = AWS.CloudWatch.Alarm("p99Latency", {
  metric: api.metrics.p99,
  threshold: 500,
  alarmActions: [pagerDuty, slackWebhook],
});

Loved by the people shipping with it

What builders say after putting Alchemy in front of their agents, their Cloudflare apps, and their production infrastructure.

Get started.

  1. Installpnpm add alchemy@latest effect
  2. Paste this into your coding agent
    Help me build an Alchemy app on Cloudflare. Start by reading https://alchemy.run/getting-started and follow it exactly: scaffold a fresh project, install the dependencies, create the `alchemy.run.ts` Stack with a single Cloudflare R2 Bucket (no Worker yet), and run `alchemy deploy` so I sign in to Cloudflare and provision the Bucket. Confirm the Bucket is live before moving on. Then STOP and ASK ME what I want to build. From there, consult only the docs you need for what I asked for — don't march me through every tutorial. A Worker only gets added later if what I want to build needs one (the tutorial covers that in part-2). Tutorial — foundations, work through whichever parts I haven't touched: https://alchemy.run/cloudflare/tutorial/part-1 First Stack (state store + first resource) https://alchemy.run/cloudflare/tutorial/part-2 Add a Worker https://alchemy.run/cloudflare/tutorial/part-3 Testing https://alchemy.run/cloudflare/tutorial/part-4 Local Dev (`alchemy dev`) https://alchemy.run/cloudflare/tutorial/part-5 CI/CD (per-PR previews from GitHub Actions) For everything else (Cloudflare deep-dives, guides, concepts), fetch https://alchemy.run/llms.txt — it's the index of the guide and concept docs. Use it to look up the specific page you need instead of guessing URLs. The per-resource API reference is indexed separately in https://alchemy.run/llms-full.txt — it's large, so only fetch it when you need a specific resource's reference page. Important: - Confirm with me before each deploy. Don't batch. - Do NOT instruct me to export CLOUDFLARE_ACCOUNT_ID or CLOUDFLARE_API_TOKEN. Alchemy stores credentials in profiles — `alchemy profile edit` (or the first `alchemy deploy`) prompts interactively for OAuth or an API token and saves it to ~/.alchemy/profiles.json. - Use pnpm: `pnpm add` to install packages and `pnpm alchemy deploy` to deploy. - If I'm migrating from Alchemy v1 (async/await), find the v1 migration guide via llms.txt and read it first.
  3. Shippnpm alchemy deploy