1---
2title: Shared database across stages
3description: Have ephemeral PR-preview stages reference a long-lived Neon or PlanetScale database from a staging stage instead of provisioning their own — fast previews, copy-on-write branches, no extra clusters.
4---
5
6[Stages](/environments/stages) make it cheap to spin up isolated copies
7of a stack — per-developer, `pr-42`, `prod`. Most resources should
8be isolated. But some — a Neon Postgres project, a shared S3
9bucket, a global rate limiter — are too expensive or too stateful
10to re-provision per stage. PR-preview stages should *point at* the
11shared instance instead.
12
13`Resource.ref(id, { stage })` reads a deployed resource's
14attributes from another stage of the same stack — typed, lazy,
15resolved at plan time against the persisted state store. See
16[References](/infrastructure-as-code/references) for the full reference surface
17(`Output.ref`, `Output.stackRef`, `Stack.stage`).
18
19The example we'll build: a Neon Postgres project that's owned by
20`staging`, with PR-preview stages (`pr-*`) referencing it instead
21of creating their own. The full file lives in
22[`examples/cloudflare-neon-drizzle/src/Db.ts`](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-neon-drizzle/src/Db.ts).
23
24## Start with the unconditional version
25
26A Neon project plus a per-stage branch. Every stage gets its own
27project — wasteful for short-lived PR previews:
28
29```typescript
30// src/Db.ts
31import * as Alchemy from "alchemy";
32import * as Drizzle from "alchemy/Drizzle";
33import * as Neon from "alchemy/Neon";
34import * as Effect from "effect/Effect";
35
36export const NeonDb = Effect.gen(function* () {
37 const schema = yield* Drizzle.Schema("app-schema", {
38 schema: "./src/schema.ts",
39 out: "./migrations",
40 });
41
42 const project = yield* Neon.Project("app-db", {
43 region: "aws-us-east-1",
44 });
45
46 const branch = yield* Neon.Branch("app-branch", {
47 project,
48 migrations: schema,
49 });
50
51 return { project, branch, schema };
52});
53```
54
55Each stage that runs this gets:
56
57- its own `Neon.Project` (a whole Postgres cluster),
58- its own `Neon.Branch` against that project,
59- migrations re-applied from scratch.
60
61Fine for `staging` and `prod`. Overkill for `pr-147`.
62
63## Read the current stage
64
65Pull the active stage from the `Stack` service so we can branch on
66it. `Alchemy.Stack` is the same handle used everywhere — it gives
67you `name`, `stage`, and friends:
68
69```diff lang="typescript"
70 export const NeonDb = Effect.gen(function* () {
71+ const { stage } = yield* Alchemy.Stack;
72+
73 const schema = yield* Drizzle.Schema("app-schema", {
74 schema: "./src/schema.ts",
75 out: "./migrations",
76 });
77```
78
79`stage` is whatever was passed to `alchemy deploy --stage <name>`.
80We'll use it to route PR previews to the shared project.
81
82## Swap in `Neon.Project.ref` for PR stages
83
84Here's the key step. Replace the unconditional `Neon.Project(...)`
85with a conditional that uses `Neon.Project.ref(...)` when the stage
86is a PR preview, and creates a real project otherwise:
87
88```diff lang="typescript"
89- const project = yield* Neon.Project("app-db", {
90- region: "aws-us-east-1",
91- });
92+ const project = stage.startsWith("pr-")
93+ ? yield* Neon.Project.ref("app-db", { stage: "staging" })
94+ : yield* Neon.Project("app-db", {
95+ region: "aws-us-east-1",
96+ });
97```
98
99Three things to know about `Resource.ref`:
100
1011. **Same logical ID.** `"app-db"` — the same logical ID the
102 `staging` stage uses to create the project. The ref lookup is
103 keyed by `{ stack, stage, id }`.
1042. **Typed.** `project` is `Neon.Project` either way — same
105 attributes, same downstream API. The branch resource doesn't
106 know or care that one stage's `project` is real and another's
107 is a reference.
1083. **Resolved at plan time.** Alchemy reads the attributes (project
109 id, host, etc.) from the staging stage's state file. If
110 `staging` hasn't been deployed yet, plan fails with
111 `InvalidReferenceError` — deploy `staging` first, then PR
112 stages can reference it.
113
114The signature is
115`Resource.ref(id, { stage?: string, stack?: string })`. Both
116options default to the current stack and stage; here we override
117`stage` to point at the long-lived shared environment.
118
119## Pass the reference downstream
120
121Once `project` exists (real or referenced), the rest of the file
122is identical. The branch creates per-stage:
123
124```typescript
125const branch = yield* Neon.Branch("app-branch", {
126 project,
127 migrations: schema,
128});
129```
130
131Each PR stage gets its own ephemeral Neon **branch** off the
132shared **project** — which is exactly the point. Branches are
133copy-on-write and free; projects aren't.
134
135## The whole picture
136
137```typescript
138// src/Db.ts
139import * as Alchemy from "alchemy";
140import * as Drizzle from "alchemy/Drizzle";
141import * as Neon from "alchemy/Neon";
142import * as Effect from "effect/Effect";
143
144export const NeonDb = Effect.gen(function* () {
145 const { stage } = yield* Alchemy.Stack;
146
147 const schema = yield* Drizzle.Schema("app-schema", {
148 schema: "./src/schema.ts",
149 out: "./migrations",
150 });
151
152 const project = stage.startsWith("pr-")
153 ? yield* Neon.Project.ref("app-db", { stage: "staging" })
154 : yield* Neon.Project("app-db", {
155 region: "aws-us-east-1",
156 });
157
158 const branch = yield* Neon.Branch("app-branch", {
159 project,
160 migrations: schema,
161 });
162
163 return { project, branch, schema };
164});
165```
166
167## Deploy
168
169Deploy `staging` once to materialize the shared project:
170
171```sh
172alchemy deploy --stage staging
173```
174
175Then PR stages can reference it:
176
177```sh
178alchemy deploy --stage pr-147
179```
180
181Plan reads `staging`'s state, resolves `app-db` against it, and
182provisions only the per-stage `Neon.Branch`. Tearing down a PR
183stage with `alchemy destroy --stage pr-147` deletes the branch but
184leaves the shared project alone — Alchemy doesn't own it from this
185stage's perspective.
186
187## The same pattern with PlanetScale
188
189`Resource.ref` isn't Neon-specific. The
190[PlanetScale](/planetscale) examples use the identical shape — a
191long-lived `PostgresDatabase` owned by a staging tier, referenced
192from `pr-*` stages that only own the ephemeral pieces (branch,
193role, Hyperdrive, Worker):
194
195```typescript
196const database = stage.startsWith("pr-")
197 ? yield* Planetscale.PostgresDatabase.ref("app-db", {
198 stage: `staging-${stage}`,
199 })
200 : yield* Planetscale.PostgresDatabase("app-db", {
201 region: { slug: "us-east" },
202 clusterSize: "PS_10",
203 });
204
205const branch = yield* Planetscale.PostgresBranch("app-branch", {
206 database,
207 migrations: schema,
208});
209```
210
211One twist worth stealing: instead of pointing every PR at a single
212global `staging`, the ref targets `staging-${stage}` — so `pr-42`
213reads from `staging-pr-42`. Each PR gets a parallel staging stage
214that owns its database, keeping PR data isolated while the
215create-vs-reference split stays the same. Deploy `staging-pr-42`
216before `pr-42`, same as the Neon flow. If you'd rather share one
217database across all PRs, use `stage: "staging"` exactly as above.
218
219The MySQL family works identically — `MySQLDatabase.ref`,
220`MySQLBranch`, `MySQLPassword`. Full files:
221[`cloudflare-planetscale-postgres-drizzle`](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-planetscale-postgres-drizzle/src/Db.ts)
222and
223[`cloudflare-planetscale-mysql-drizzle`](https://github.com/alchemy-run/alchemy/tree/main/examples/cloudflare-planetscale-mysql-drizzle/src/Db.ts).
224
225## Cross-stack too
226
227`Resource.ref` also takes a `stack` option for pulling a resource
228out of a *different* stack:
229
230```typescript
231const project = yield* Neon.Project.ref("app-db", {
232 stack: "shared-infra",
233 stage: "prod",
234});
235```
236
237The lookup is `{ stack, stage, id }` — change any of them and
238you're pointing at a different deployed resource. For pulling a
239whole stack's outputs (instead of a single resource) see the
240multi-stack section of [Monorepos](/project-structure/monorepo).
241
242## When to use which
243
244| Goal | Use |
245| ------------------------------------------- | ---------------------------------------------- |
246| Reference a single resource, same stack, different stage | `Resource.ref(id, { stage })` |
247| Reference a single resource, different stack | `Resource.ref(id, { stack, stage })` |
248| Reference an entire stack's outputs | `Backend.stage[name]` ([guide](/project-structure/monorepo)) |
249| Reference an arbitrary deployed Output | [`Output.ref`](/infrastructure-as-code/outputs#ref) |
250
251All four resolve through the same state store — they just differ
252in what shape they return.
253
254## Related
255
256- [Resource](/infrastructure-as-code/resource#ref) — `Resource.ref` reference.
257- [Stages](/environments/stages) — naming conventions for `pr-*`,
258 `dev_*`, `staging`, `prod`.
259- [Monorepos](/project-structure/monorepo) — whole-stack references via
260 typed `Stack` handles.
261- [Neon](/neon) and [PlanetScale](/planetscale) — the two database
262 providers used in this guide.