t3-code-android-nightly/.repos/alchemy-effect/examples/prisma-tanstack-start/alchemy.run.ts
Julius Marminge e3c85ead63
chore(refs): sync Effect and Alchemy references to rc.115 and beta.78 (#12327)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 23:21:25 -07:00

162 lines
6.3 KiB
TypeScript

import * as Alchemy from "alchemy";
import * as Cloudflare from "alchemy/Cloudflare";
import * as Command from "alchemy/Command";
import * as Output from "alchemy/Output";
import * as Prisma from "alchemy/Prisma";
import * as Config from "effect/Config";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Redacted from "effect/Redacted";
const tanstackMessageConfig = (fallback: string) =>
Config.String("TANSTACK_MESSAGE").pipe(Effect.orElseSucceed(() => fallback));
export default Alchemy.Stack(
"PrismaTanstackStart",
{
providers: Layer.mergeAll(Prisma.providers(), Command.providers()),
state: Cloudflare.state(),
},
Effect.gen(function* () {
const stage = yield* Alchemy.Stage;
const appName = yield* Config.String("PRISMA_TANSTACK_APP").pipe(
Effect.orElseSucceed(() => `alchemy-prisma-tanstack-start-${stage}`),
);
const customDomainHostname = yield* Config.String(
"PRISMA_TANSTACK_DOMAIN",
).pipe(Effect.orElseSucceed(() => undefined));
const devPort = yield* Config.Number("PRISMA_TANSTACK_DEV_PORT").pipe(
Effect.orElseSucceed(() => 3000),
);
// Project is the Prisma Console container for everything else in this
// stack: its default branch, databases, connections, env vars, and Apps.
// `createDatabase: false` keeps the example explicit so the database below
// is visible as its own Alchemy resource.
const project = yield* Prisma.Project("Project", {
name: yield* Config.String("PRISMA_PROJECT").pipe(
Effect.orElseSucceed(() => undefined),
),
createDatabase: false,
region: "eu-west-3",
});
// Postgres is the product-shaped alias for Prisma.Database. In deploy it
// creates a real Prisma Postgres database and attaches it to the default
// `main` branch created transactionally with the project. In `alchemy dev`,
// the `dev` block starts a local Prisma Postgres via `@prisma/dev` and
// returns the same connection-string attributes.
const postgres = yield* Prisma.Postgres("Postgres", {
project,
region: "eu-west-3",
branchGitName: "main",
dev: {
provider: "@prisma/dev",
// Local dev should boot into a useful app, not an empty shell. This
// command runs after @prisma/dev is ready, with DATABASE_URL pointed at
// the direct local Postgres URL.
migrate: "bun run db:setup",
},
});
// Connection creates the app-facing database credentials. Its returned
// strings are redacted in Alchemy state. In dev mode it mirrors the local
// `@prisma/dev` URLs from Postgres, so the app keeps the same env names.
const connection = yield* Prisma.Connection("Connection", {
database: postgres,
});
// Apply checked-in, transactional Prisma Next migrations before building a
// deployment. Apply is idempotent and intentionally runs every deploy so a
// restored or drifted database is verified instead of trusting local state.
// Production data is never seeded here.
const databaseSchema = yield* Command.Exec("DatabaseSchema", {
command: "bun run db:migrate",
cwd: ".",
env: {
DATABASE_URL:
connection.directConnectionString.as<Redacted.Redacted<string>>(),
// Bound server-side lock waits and statement execution independently
// from the release-process deadline below.
PGOPTIONS: "-c lock_timeout=30s -c statement_timeout=5min",
// The release subprocess needs only the database credential. Shadow
// the IaC process's Management API credentials so schema tooling and
// any of its plugins cannot read the workspace-scoped service token.
PRISMA_SERVICE_TOKEN: "",
PRISMA_API_TOKEN: "",
},
memo: false,
timeout: "6 minutes",
});
// Compute builds the TanStack Start app, uploads the `.output` artifact
// to Prisma Compute, and promotes a Deployment. The env block is the runtime
// contract for deploy; the dev block runs Vite locally while reusing the
// same database/project IDs generated by the resources above.
const app = yield* Prisma.Compute("App", {
project,
appName,
regionId: "eu-west-3",
branchGitName: "main",
path: ".",
build: {
command: "bun run build",
outdir: ".output",
entrypoint: "server/index.mjs",
env: {
// Creates an explicit dependency on successful schema convergence
// without exposing the migration credential to the build.
ALCHEMY_SCHEMA_HASH: databaseSchema.hash.pipe(
Output.map((hash) => hash.input ?? "unmemoized"),
),
},
},
port: 3000,
healthCheck: { path: "/api/health" },
env: {
// The deployed application receives only the pooled credential it
// uses. The direct credential is scoped to the schema command above.
DATABASE_URL: connection.databaseUrl,
TANSTACK_MESSAGE: yield* tanstackMessageConfig(
"hello from TanStack Start on Prisma Compute",
),
// Keep values consumed by this deployment in Compute.env so they are
// reconciled before the deployment snapshots its branch environment.
TANSTACK_SHARED_FLAG: Redacted.make("project-level"),
},
dev: {
command: "bun run dev:start",
port: devPort,
env: {
TANSTACK_MESSAGE: yield* tanstackMessageConfig(
"hello from local alchemy dev",
),
TANSTACK_SHARED_FLAG: "local-project-level",
PRISMA_TANSTACK_PG_POOL_MAX: "1",
},
},
destroyOldDeployment: true,
});
const customDomain = customDomainHostname
? yield* Prisma.CustomDomain("CustomDomain", {
app: app.appId,
hostname: customDomainHostname,
})
: undefined;
return {
projectId: project.projectId,
branchId: postgres.branchId,
databaseId: postgres.databaseId,
connectionId: connection.connectionId,
appId: app.appId,
deploymentId: app.deploymentId,
url: app.url,
customDomainId: customDomain?.customDomainId,
customDomainHostname: customDomain?.hostname,
customDomainStatus: customDomain?.status,
customDomainDnsRecords: customDomain?.dnsRecords,
};
}),
);