Prisma.ORM reference
Contract
Section titled “Contract”Source:
src/Prisma/ORM/Contract.ts
A Prisma ORM v8 contract managed as an Alchemy resource.
Runs prisma contract emit and — when the contract’s storage shape
has drifted from the migration graph — prisma migration plan as
part of alchemy deploy, so the emitted contract artifacts
(contract.json / contract.d.ts) and the migration packages under
migrations/ are always regenerated from the source contract before
anything downstream deploys. Pair it with Migrate to apply the
planned packages to a database in the same deploy:
const contract = yield* Prisma.Contract("contract");
const migrate = yield* Prisma.Migrate("migrate", { url: branch.origin.connectionString, contract,});Plans that require a human decision (data backfills rendered as
placeholder(...) closures in migration.ts) are never auto-answered:
the deploy fails with instructions to fill the placeholder, self-emit the
package (node migrations/app/<dir>/migration.ts), and commit — the next
deploy then sees no drift and applies the committed package.
The resource is delete-safe: removing it from the stack does not wipe the migrations directory or the emitted contract, since both are checked in and shared with other environments.
Contract: Declaring the contract
Section titled “Contract: Declaring the contract”Contract at the project root
// expects ./prisma.config.ts and writes ./migrationsconst contract = yield* Prisma.Contract("contract");Custom config location
const contract = yield* Prisma.Contract("contract", { config: "./db/prisma.config.ts", // resolved relative to ./db (the config's directory) migrationsDir: "./migrations",});Contract: Applying migrations on deploy
Section titled “Contract: Applying migrations on deploy”const contract = yield* Prisma.Contract("contract");const project = yield* Neon.Project("db");const branch = yield* Neon.Branch("main", { project });
yield* Prisma.Migrate("migrate", { url: branch.origin.connectionString, contract,});Migrate
Section titled “Migrate”Source:
src/Prisma/ORM/Migrate.ts
Applies pending Prisma migration packages to a Postgres database
during alchemy deploy — the deploy-graph equivalent of running
prisma db migrate --db $DATABASE_URL by hand.
The apply is idempotent: Prisma records the applied contract in the
database’s prisma_contract.marker table and only walks the pending part
of the migration graph, so re-deploys are no-ops and a database
provisioned in the same deploy is bootstrapped from empty. Fresh databases
that are still coming up are retried briefly (bounded) before failing.
Destroying the resource never touches the database — dropping tables is not the IaC engine’s call to make.
Migrate: Migrating a database on deploy
Section titled “Migrate: Migrating a database on deploy”Neon branch
const contract = yield* Prisma.Contract("contract");const project = yield* Neon.Project("db");const branch = yield* Neon.Branch("main", { project });
yield* Prisma.Migrate("migrate", { url: branch.origin.connectionString, contract,});Prisma Postgres
const contract = yield* Prisma.Contract("contract");const database = yield* Prisma.Database("db", { project });
yield* Prisma.Migrate("migrate", { url: database.directConnectionString, contract,});Pinning an environment to a contract ref
yield* Prisma.Migrate("migrate", { url: branch.origin.connectionString, contract, to: "production",});Postgres
Section titled “Postgres”Source:
src/Prisma/ORM/Postgres.ts
Open a Prisma ORM v8 Postgres client from a connection URL, with Effect-native query surfaces over Prisma’s own builders and engine.
The client is built at most once per execution — a Worker
fetch/queue/scheduled event, a Durable Object call, a Workflow run,
or a Lambda invocation — and memoized on the execution’s Scope (via
makeExecutionMemo), with close() registered as a scope finalizer
so the underlying pg pool never outlives its event. That per-execution
lifecycle is what makes the client safe on workerd, where sockets are
pinned to the creating request’s IoContext. Construction does no I/O
(Prisma connects lazily on the first query), so deploy/plan-time
evaluations never touch the database.
Model types infer directly from the native TypeScript contract, together
with the connection source’s error and requirement channels. No generated
application imports are required. For Prisma rc.11, author contracts with
defineContract from alchemy/Prisma/ORM to retain metadata
lost by the upstream declarations. It uses Prisma’s native runtime builders.
import * as PrismaPostgres from "alchemy/Prisma/ORM/Postgres";import { contract } from "./prisma/contract.ts";
const connection = yield* Cloudflare.Hyperdrive.Connect(hyperdrive);const db = yield* PrismaPostgres.Postgres( connection.connectionString, { contract },);
fetch: Effect.gen(function* () { // orm lane — queries ARE Effects, with typed errors const user = yield* db.orm.public.User.where({ email }).include("posts").first(); const made = yield* db.orm.public.Post.create({ title, authorId: user.id });
// sql builder lane — pure plans, Effect executor const rows = yield* db.execute(db.sql.public.user.select("id", "email").build());
// transactions — commit on success, rollback on failure/interrupt yield* db.transaction((tx) => Effect.gen(function* () { const u = yield* tx.orm.public.User.create({ email }); if (!u) return yield* tx.rollback(); return u; }), );});Postgres: PSL Contracts
Section titled “Postgres: PSL Contracts”import { makeDatabase } from "./prisma/generated/client.ts";const db = yield* makeDatabase(connection.connectionString);const users = yield* db.orm.public.User.select("id", "email").all();Run alchemy prisma generate with withEffect registered in the ORM
configuration. The factory delegates to this runtime with Prisma’s emitted
Contract type and contractJson; query behavior and cleanup are identical.
Generated schemas.ts can be imported independently of this client.
Postgres: Prepared Queries
Section titled “Postgres: Prepared Queries”const findUser = yield* db.prepare({ email: "pg/text@1" }, (sql, params) => sql.public.user .select("id", "email") .where((fields, fns) => fns.eq(fields.email, params.email)) .build(),);const users = yield* findUser.query({ email: "alice@example.com" });const rows = findUser.query({ email: "alice@example.com" }).stream;Prepared queries resolve the current execution’s client for each run. Inside
db.transaction, tx.prepare binds executions to that transaction instead.
An affected-count plan returns a prepared mutation with execute(params);
a row-returning plan returns a prepared query with query(params).
The Stream surfaces use Prisma’s async iterators. The long-lived Postgres driver buffers raw results, so these streams do not guarantee bounded memory.
Queries are lazy and re-runnable: each evaluation replays the chain
against the execution’s client, so Effect.retry re-issues the query.
Failures surface as granular tagged errors: the SQL-standard integrity
violations each get their own tag (Prisma.UniqueViolationError,
Prisma.ForeignKeyViolationError, Prisma.NotNullViolationError,
Prisma.CheckViolationError), other statement failures are
Prisma.QueryError (with the normalized sqlState), connection
failures are Prisma.ConnectionError (with the driver’s
transient verdict), and Prisma’s structured codes split by
category into Prisma.OrmError / Prisma.RuntimeError
with an autocompleting code field.