Multiple Stacks
Each package owns its own alchemy.run.ts
(examples/monorepo-multi-stack):
.├── package.json # workspaces: ["frontend", "backend"]├── backend/│ ├── alchemy.run.ts # Backend Stack — deploys the Worker│ ├── package.json # exports "." and "./Client"│ └── src/│ ├── Spec.ts # HttpApi schema│ ├── Service.ts # the Worker│ ├── Client.ts # typed HttpApiClient factory│ ├── Stack.ts # typed Stack handle — { url: string }│ └── index.ts # re-exports Spec + Client + Stack└── frontend/ ├── alchemy.run.ts # Frontend Stack — yield* Backend ├── package.json # depends on "backend": "workspace:*" └── src/ └── main.tsx # React app — calls BackendClient(VITE_API_URL)The packages themselves — workspace root, Spec.ts,
Service.ts, Client.ts, the ./Client subpath, the React
entry — are identical to the
Single Stack layout. This page
covers what changes: two Stacks wired by a typed reference,
resolved at plan time against the state store. When to reach for
this shape is covered in
References.
Declare the typed Stack handle
Section titled “Declare the typed Stack handle”import * as Alchemy from "alchemy";
export class Backend extends Alchemy.Stack< Backend, { url: string; }>()("Backend") {}Alchemy.Stack<Self, Outputs>()(name) names the Stack, declares
its output shape (TypeScript enforces it on both sides), and
exposes .make(...) to deploy plus .stage[name] to reference a
deployed stage.
Re-export the handle
Section titled “Re-export the handle”export * from "./Client.ts";export * from "./Spec.ts";export * from "./Stack.ts";import { Backend } from "backend" now resolves the typed
handle. The browser still imports through "backend/Client", so
adding Stack.ts to the barrel does not affect the React bundle.
Deploy the backend with Backend.make
Section titled “Deploy the backend with Backend.make”import * as Cloudflare from "alchemy/Cloudflare";import * as Effect from "effect/Effect";import Service from "./src/Service.ts";import { Backend } from "./src/Stack.ts";
export default Backend.make( { providers: Cloudflare.providers(), state: Cloudflare.state(), }, Effect.gen(function* () { const api = yield* Service; return { url: api.url.as<string>(), }; }),);Backend.make is a typed shorthand for Alchemy.Stack — if the
returned object doesn’t match { url: string }, the file fails
to typecheck.
Reference it from the frontend
Section titled “Reference it from the frontend”import * as Alchemy from "alchemy";import * as Cloudflare from "alchemy/Cloudflare";import { Backend } from "backend";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "Frontend", { providers: Cloudflare.providers(), state: Cloudflare.state(), }, Effect.gen(function* () { const backend = yield* Backend;
const website = yield* Cloudflare.Website.Vite("Website", { env: { VITE_API_URL: backend.url, }, });
return { url: website.url.as<string>(), }; }),);yield* Backend resolves to the same stage of the named
Stack that the frontend is being deployed to — sam frontend
reads sam backend, pr-42 reads pr-42. Under the hood it’s
Output.stackRef reading the backend’s
persisted output from the state store.
Deploy in order
Section titled “Deploy in order”cd backend && alchemy deploy --stage samcd frontend && alchemy deploy --stage samThe backend must be deployed to the same stage first; otherwise
the frontend’s plan fails with InvalidReferenceError. Destroy
in reverse — frontend first, then backend.
Pin to a specific stage
Section titled “Pin to a specific stage”The bare yield* Backend is the right default. To break stage
symmetry — e.g. every frontend stage points at the production
backend — pin with Backend.stage.<name>:
Effect.gen(function* () { const backend = yield* Backend; const backend = yield* Backend.stage.prod; // ...})Backend.stage is a proxy keyed by stage name — any string
works (Backend.stage.staging, Backend.stage["pr-42"]). Which
form to use when:
References.
Where next
Section titled “Where next”- Monorepo — the high-level chooser.
- Single Stack — the shared package layout and the one-Stack alternative.
- References — when to split Stacks, stage pinning, and the pattern → tool table.
- References — how a reference resolves:
typing, plan-time state reads,
InvalidReferenceError.