| 1 | --- |
| 2 | title: References |
| 3 | description: Read an already-deployed Resource or Stack at plan time — Resource.ref by Logical ID with optional stack/stage props, or import a Stack tag and depend on its outputs. |
| 4 | --- |
| 5 | |
| 6 | A reference reads something **already deployed** — a different |
| 7 | stage, a different Stack, or both — from the persisted |
| 8 | [state store](/state-store) at plan time. References are |
| 9 | lazy and don't drift: every plan re-reads the upstream's state, so |
| 10 | downstream always sees whatever was actually last deployed. |
| 11 | |
| 12 | ## Reference another stage |
| 13 | |
| 14 | `Resource.ref` takes a |
| 15 | [Logical ID](/infrastructure-as-code/resource#logical-id); pass |
| 16 | `stage` to read that Resource from a different stage of this stack: |
| 17 | |
| 18 | ```typescript |
| 19 | const { stage } = yield* Alchemy.Stack; |
| 20 | |
| 21 | const project = stage.startsWith("pr-") |
| 22 | ? yield* Neon.Project.ref("app-db", { stage: "staging" }) |
| 23 | : yield* Neon.Project("app-db", { region: "aws-us-east-1" }); |
| 24 | ``` |
| 25 | |
| 26 | The ref has the same type as `yield* Neon.Project("app-db", { … })` |
| 27 | — pass it anywhere the real thing is accepted. Here `staging` owns |
| 28 | the database and `pr-*` stages borrow it; destroying a borrower |
| 29 | never touches the Resources it references. |
| 30 | |
| 31 | ## Reference another Stack |
| 32 | |
| 33 | Pass `stack` too for a Resource in a different Stack: |
| 34 | |
| 35 | ```typescript |
| 36 | const sharedBucket = yield* Storage.Bucket.ref("Assets", { |
| 37 | stack: "shared-infra", |
| 38 | stage: "prod", |
| 39 | }); |
| 40 | ``` |
| 41 | |
| 42 | `{ stack, stage, id }` is the full address; anything you omit |
| 43 | defaults to the current stack/stage. Pin `stage` when the stacks' |
| 44 | stages don't line up — `pr-147` has no counterpart in |
| 45 | `shared-infra`. |
| 46 | |
| 47 | ## Depend on a Stack's outputs |
| 48 | |
| 49 | When the unit you consume is a whole Stack, import its tag and |
| 50 | yield it: |
| 51 | |
| 52 | ```typescript |
| 53 | // backend/src/Stack.ts |
| 54 | export class Backend extends Alchemy.Stack< |
| 55 | Backend, |
| 56 | { url: string } |
| 57 | >()("Backend") {} |
| 58 | ``` |
| 59 | |
| 60 | ```typescript |
| 61 | // inside the frontend Stack's Effect.gen |
| 62 | const a = yield* Backend; // current stage |
| 63 | const b = yield* Backend.stage.prod; // pinned to "prod" |
| 64 | const c = yield* Backend.stage["pr-42"]; // any stage name |
| 65 | ``` |
| 66 | |
| 67 | Every successful `apply` persists what the Stack's effect returned |
| 68 | as a per-stage output record; `yield* Backend` reads it back as a |
| 69 | proxy of typed Outputs, so `(yield* Backend).url` is |
| 70 | `Output<string>`. Full two-package walkthrough: |
| 71 | [Multiple Stacks](/project-structure/monorepo-multi-stack). |
| 72 | |
| 73 | ## Escape hatches |
| 74 | |
| 75 | When the Resource constructor isn't in scope, `Output.ref` is the |
| 76 | same lookup: |
| 77 | |
| 78 | ```typescript |
| 79 | const bucket = Output.ref<typeof Bucket>("Bucket", { |
| 80 | stack: "shared-infra", |
| 81 | stage: "prod", |
| 82 | }); |
| 83 | ``` |
| 84 | |
| 85 | When a typed Stack handle isn't in scope, `Output.stackRef` reads |
| 86 | the output record directly: |
| 87 | |
| 88 | ```typescript |
| 89 | const backend = yield* Output.stackRef<{ url: string }>("Backend", { |
| 90 | stage: "prod", |
| 91 | }); |
| 92 | ``` |
| 93 | |
| 94 | Prefer `Resource.ref` and `yield* MyStack` — same lookups, real |
| 95 | type bindings. |
| 96 | |
| 97 | ## Deploy order |
| 98 | |
| 99 | The upstream must already be deployed to the exact |
| 100 | `{ stack, stage }` the reference names, or the plan fails fast |
| 101 | with a typed `InvalidReferenceError` — there is no |
| 102 | deploy-and-hope path: |
| 103 | |
| 104 | ```sh |
| 105 | alchemy deploy --stage staging # creates app-db |
| 106 | alchemy deploy --stage pr-147 # borrows staging's app-db |
| 107 | ``` |
| 108 | |
| 109 | Destroy in reverse order. Resolution itself is the in-stack |
| 110 | [Output](/infrastructure-as-code/outputs) flow with one difference: a reference's |
| 111 | upstream was reconciled by a previous deploy, so only its persisted |
| 112 | attributes participate in this plan. |
| 113 | |
| 114 | ## Where next |
| 115 | |
| 116 | - [Resource Lifecycle](/infrastructure-as-code/resource-lifecycle) — how |
| 117 | the referenced state got persisted in the first place. Next page. |
| 118 | - [Multiple Stacks](/project-structure/monorepo-multi-stack) — typed Stack |
| 119 | handles wiring a monorepo. |
| 120 | - [Shared database across stages](/cloudflare/data/shared-database) — |
| 121 | the owner/borrower pattern end to end. |
| 122 | - [Inputs & Outputs](/infrastructure-as-code/outputs) — the Output model |
| 123 | references plug into. |
| 124 | - [State Store](/state-store) — where the persisted state |
| 125 | that refs read lives. |