| 1 | import * as Alchemy from "alchemy"; |
| 2 | import * as Cloudflare from "alchemy/Cloudflare"; |
| 3 | import * as Drizzle from "alchemy/Drizzle"; |
| 4 | import * as Neon from "alchemy/Neon"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | |
| 7 | /** |
| 8 | * A Drizzle schema + Neon project + feature branch. The branch's |
| 9 | * `migrations` prop is wired to the schema resource, so the |
| 10 | * provider order becomes: |
| 11 | * |
| 12 | * 1. `Drizzle.Schema` regenerates pending migration SQL files. |
| 13 | * 2. `Neon.Branch` scans the directory and applies any new migrations |
| 14 | * transactionally. |
| 15 | */ |
| 16 | export const NeonDatabase = Effect.gen(function* () { |
| 17 | const { stage } = yield* Alchemy.Stack; |
| 18 | |
| 19 | const schema = yield* Drizzle.Schema("Schema", { |
| 20 | schema: "./src/backend/schema.ts", |
| 21 | out: "./migrations", |
| 22 | }); |
| 23 | |
| 24 | const project = stage.startsWith("pr-") |
| 25 | ? yield* Neon.Project.ref("Database", { stage: `staging-${stage}` }) |
| 26 | : yield* Neon.Project("Database", { |
| 27 | region: "aws-us-east-1", |
| 28 | }); |
| 29 | |
| 30 | const branch = yield* Neon.Branch("Branch", { |
| 31 | project, |
| 32 | migrations: schema, |
| 33 | }); |
| 34 | |
| 35 | return { project, branch, schema }; |
| 36 | }); |
| 37 | |
| 38 | /** |
| 39 | * A Hyperdrive pool in front of the Neon branch. The backend worker binds to |
| 40 | * this so Drizzle talks to Postgres through Cloudflare's connection pooler. |
| 41 | */ |
| 42 | export const Hyperdrive = Effect.gen(function* () { |
| 43 | const { branch } = yield* NeonDatabase; |
| 44 | |
| 45 | return yield* Cloudflare.Hyperdrive.Connection("Hyperdrive", { |
| 46 | origin: branch.origin, |
| 47 | |
| 48 | // By default, Hyperdrive caches responses for 60 seconds. |
| 49 | // In this case, we wanted database writes to be reflected immediately, |
| 50 | // so we disabled caching while retaining Hyperdrive for connection pooling. |
| 51 | // You should configure this based on your requirements. |
| 52 | // Another common approach is to use two Hyperdrive connections: one with caching enabled |
| 53 | // for use cases that can accept eventual consistency in exchange for performance, and one |
| 54 | // without for when you need immediate consistency. |
| 55 | caching: { |
| 56 | disabled: true, |
| 57 | }, |
| 58 | }); |
| 59 | }); |