# Prisma

> Adapt a Prisma client and enqueue jobs inside caller-owned interactive transactions.

Your application already writes through Prisma. When a request creates an account and enqueues a
follow-up job, those two writes must commit together — a job for a row that rolled back is a bug,
and a committed row with no job is a silent gap. `@stablemates/workhorse-prisma` closes that gap: it converts
Prisma clients and interactive transaction clients into the core `Queryable` protocol, so
`Queue.enqueue` runs inside the same transaction as your Prisma writes.

Workhorse keeps its schema lifecycle outside Prisma migrations. Prisma owns your models; Workhorse
owns its versioned SQL functions.

## Create the adapter

Install the integration beside Prisma Client.

```bash
pnpm add @stablemates/workhorse-prisma @prisma/client
```

Pass the client to `createPrismaAdapter`. The close callback is optional because the adapter does
not disconnect caller-owned clients by default.

```ts
import { PrismaClient } from "@prisma/client";
import { createPrismaAdapter } from "@stablemates/workhorse-prisma";

const prisma = new PrismaClient();
const workhorse = createPrismaAdapter(prisma, {
  close: () => prisma.$disconnect(),
});
```

The returned `WorkhorseAdapter` exposes `database`, `queue`, `admin`, `forTransaction`,
`adminForTransaction`, `createWorker`, and `close` — one stable surface for request handlers,
operator tools, and worker processes.

## Enqueue inside an interactive transaction

This is the pattern the adapter exists for. Call `forTransaction` with the callback's transaction
client. The queue sends its SQL through that client, so the job follows the surrounding commit or
rollback.

```ts
await prisma.$transaction(async (tx) => {
  const account = await tx.account.create({ data: { email } });
  await workhorse.forTransaction(tx).enqueue("account.created", {
    accountId: account.id,
  });
});
```

If the callback throws, Prisma rolls back both writes. Nothing outside the transaction ever sees a
half-committed pair. The adapter's default queue remains outside that transaction — use it for
enqueues that stand alone.

## Enable notification-assisted workers

Prisma does not expose a stable node-postgres pool. Supply a separate caller-owned pool as
`notificationPool` when workers should reserve a connection for `LISTEN/NOTIFY` and pick up new
jobs immediately. Without it, workers use bounded polling and keep the same durable behavior — jobs
still run, just on the polling cadence.

Database execution failures become `PrismaQueryError`, which retains the statement, original cause,
and nested PostgreSQL code when Prisma provides one. Typed Workhorse conflicts — idempotency,
checkpoint, and wait errors — remain their core error classes.

## Keep resource ownership explicit

Unless you supply `close`, the adapter leaves the Prisma client connected. Add that hook only when
the adapter owns the client, because `adapter.close` may run during process shutdown or failed
startup.

Install the Workhorse schema with the core deployment tools. Prisma migrations should not manage
Workhorse's versioned SQL functions — the schema is a versioned protocol, not application models.

## Next

- [Enqueue and transactions](/docs/enqueue) — understand the transaction guarantee
- [Workers](/docs/workers) — configure polling and handlers
- [Installation](/docs/installation) — install the schema outside Prisma migrations

---

Exact adapter boundary and transaction ownership:
[architecture reference](https://github.com/stablemates/workhorse/blob/main/docs/architecture.md#system-context).
