TypeORM adapter
Declarative @Transactional() for NestJS + TypeORM. Keep
@InjectRepository(Entity), add one decorator — transactions propagate through
CLS (AsyncLocalStorage) across services. Standard NestJS dependency injection
built on the actively maintained
@nestjs-cls/transactional:
no monkey-patching. Inspired by
typeorm-transactional —
a decorator-based approach many NestJS developers already know, but that is no
longer maintained.
Install
npm install @nestjs-transactions/typeorm @nestjs-transactions/core \
@nestjs/typeorm typeorm @nestjs-cls/transactional \
@nestjs-cls/transactional-adapter-typeorm nestjs-cls
(All are peer dependencies — this package ships zero runtime dependencies. @nestjs/common and @nestjs/core are peers too, but every NestJS app already has them.)
Quick start
Use NestjsTypeormModule from this package instead of @nestjs/typeorm's
TypeOrmModule — one unified module owns both the database connection and
transaction propagation:
// app.module.ts
import { NestjsTypeormModule } from '@nestjs-transactions/typeorm';
@Module({
imports: [NestjsTypeormModule.forRoot({/* all @nestjs/typeorm options ... */})],
})
export class AppModule {}
forRoot() accepts everything @nestjs/typeorm's does (autoLoadEntities,
retryAttempts, name, …) — it delegates DataSource creation to
@nestjs/typeorm internally — plus the transactional options
defaultTxOptions and enableTransactionProxy. It also registers the
@nestjs-cls/transactional CLS plugin that powers @Transactional()
(starting/committing/rolling back transactions and swapping the active
EntityManager).
// member.module.ts — same shape as @nestjs/typeorm's forFeature
@Module({
imports: [NestjsTypeormModule.forFeature([Member])],
providers: [MemberService, AccountingService],
})
export class MemberModule {}
// member.service.ts — completely vanilla NestJS + TypeORM
// (InjectRepository is re-exported — @nestjs/typeorm's symbol, one import)
import { InjectRepository, Transactional } from '@nestjs-transactions/typeorm';
@Injectable()
export class MemberService {
constructor(
@InjectRepository(Member) private readonly repo: Repository<Member>,
private readonly accounting: AccountingService,
) {}
@Transactional()
async register(name: string) {
const member = await this.repo.save({ name });
await this.accounting.openAccount(member); // joins the SAME transaction —
return member; // no decorator needed there
}
}
If register throws, everything rolls back — including writes made in
AccountingService. Outside a transaction the repository behaves like a plain
TypeORM repository.
How it works
forFeature([Member]) registers a provider under TypeORM's standard repository
token — the exact token @InjectRepository resolves — whose value is a lazy
proxy over txHost.tx.getRepository(Member). txHost.tx is the transactional
EntityManager inside @Transactional() and the regular one outside. No
prototypes are patched; it is ordinary NestJS dependency injection. See
Concepts for the full picture.
On this section
- Propagation — the seven propagation modes.
- Isolation levels — set defaults and per-call isolation.
- Multiple data sources — named connections.
- Transaction hooks — run code after commit/rollback.
- Programmatic control —
TransactionHostwithout the decorator. - Custom repositories —
NestjsTypeormRepository. - Testing — the no-op module for unit tests.
- Migrating from typeorm-transactional.
- Caveats.