TypeORM
Adapt a TypeORM data source and enqueue jobs through transactional entity managers.
Your application already writes through TypeORM. When a request saves an entity 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-typeorm closes that gap: it converts
TypeORM data sources and transactional entity managers into the core Queryable protocol, so
Queue.enqueue runs inside the same transaction as your entity writes.
Workhorse keeps its schema lifecycle outside TypeORM migrations. TypeORM owns your entities; Workhorse owns its versioned SQL functions.
Create the adapter
Install the integration beside TypeORM.
pnpm add @stablemates/workhorse-typeorm typeormPass an initialized data source to createTypeOrmAdapter. The close callback is optional because
the adapter does not destroy caller-owned data sources by default.
import { createTypeOrmAdapter } from "@stablemates/workhorse-typeorm";
const workhorse = createTypeOrmAdapter(dataSource, {
close: () => dataSource.destroy(),
});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 a transaction
This is the pattern the adapter exists for. Call forTransaction with the callback's entity
manager. The queue sends its SQL through that manager, so the job follows the surrounding commit or
rollback.
await dataSource.transaction(async (manager) => {
const account = await manager.save(Account, { email });
await workhorse.forTransaction(manager).enqueue("account.created", {
accountId: account.id,
});
});If the callback throws, TypeORM 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
TypeORM does not expose its internal node-postgres pool as a stable API. 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 TypeOrmQueryError, which retains the statement, original
cause, and PostgreSQL driver code. 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 data source initialized. Add that hook only when
the adapter owns the data source, because adapter.close may run during process shutdown or failed
startup.
Install the Workhorse schema with the core deployment tools. TypeORM migrations should not manage Workhorse's versioned SQL functions — the schema is a versioned protocol, not application entities.
Next
- Enqueue and transactions — understand the transaction guarantee
- Workers — configure polling and handlers
- Installation — install the schema outside TypeORM migrations
Exact adapter boundary and transaction ownership: architecture reference.