npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nestjs-transactional/typeorm

v2.0.0

Published

TypeORM adapter for @nestjs-transactional/core — EntityManager propagation, savepoints, multi-datasource

Readme

@nestjs-transactional/typeorm

npm version License: MIT

TypeORM adapter for @nestjs-transactional/core.

Two things come with it. The adapter itself, which maps @Transactional() onto TypeORM's transactions and savepoints — and transparent transactional repositories: your existing @InjectRepository(Order) instances start honouring the active transaction on their own, with no change to the code that uses them.

@Injectable()
export class OrderService {
  constructor(@InjectRepository(Order) private readonly orders: Repository<Order>) {}

  @Transactional()
  async place(dto: PlaceOrderDto) {
    // Runs in the transaction. Rolls back if anything below throws.
    // Outside a @Transactional method, the same call autocommits.
    return this.orders.save(dto);
  }
}

No getCurrentEntityManager(), no passing an EntityManager down through service layers, no separate "transactional" repository type.

Install

pnpm add @nestjs-transactional/typeorm @nestjs-transactional/core typeorm @nestjs/typeorm reflect-metadata

Module format

This package ships ESM only, matching NestJS 12, which is ESM-only across its own packages. There is no CommonJS build.

A CommonJS application still works: Node loads ESM from require() since 22.12.0, which is why engines.node is >=22.13.0. What does not follow Node here is tooling with its own module loader — Jest above all, which needs NODE_OPTIONS=--experimental-vm-modules and a few config settings. The 19 example applications in the repository all run their suites that way and can be copied from.

Reasoning and measurements: ADR-022.

Quick start

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TransactionalModule } from '@nestjs-transactional/core';
import { TypeOrmTransactionalModule } from '@nestjs-transactional/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({ type: 'postgres', entities: [Order] }),
    TypeOrmModule.forFeature([Order]),

    TransactionalModule.forRoot({ isGlobal: true }),
    TypeOrmTransactionalModule.forRoot(),
  ],
})
export class AppModule {}

TransactionalModule.forRoot({ isGlobal: true }) has to be there, with isGlobal — that is how this module sees the core registry from its own DI scope. The DataSource is found through @nestjs/typeorm's getDataSourceToken(name), the same convention @InjectRepository(E, dataSource) uses, so nothing else needs wiring.

What becomes transparent

These all dispatch through the active transaction:

  • @InjectRepository(Entity) — the common case.
  • @InjectEntityManager() em.getRepository(E).
  • @InjectDataSource() ds.getRepository(E) and ds.manager.
  • Custom repositories built with Repository.extend(...).
  • TreeRepository and MongoRepository, which inherit from Repository.

Two patterns are not covered, and need an escape hatch:

  1. em.save(Entity, ...) called directly on an injected EntityManager. The patch covers em.getRepository(E).save(...), not the manager's own data methods — patching all ~14 of them would require per-method recursion guards, which was judged not worth the surface area.
  2. BaseEntity static methods (User.save(...)). BaseEntity.useDataSource(...) captures a DataSource reference that bypasses the patch. The library typeorm-transactional has the same limitation.

For both, either use a repository or reach for the escape hatch:

import { getCurrentEntityManager } from '@nestjs-transactional/typeorm';

@Transactional()
async runRawSql() {
  // Pass the DataSource as fallback so this also works outside a
  // transaction, where it returns ds.manager.
  const em = getCurrentEntityManager('default', this.ds);
  await em.query('UPDATE accounts SET balance = balance - $1', [100]);
}

Dialect-dependent behaviour

Two options behave differently per database, and both fail loudly or harmlessly rather than surprisingly:

  • readOnly is enforced on postgres, cockroachdb and aurora-postgres, where the adapter issues SET TRANSACTION READ ONLY as the transaction's first statement and the database refuses a write. On other dialects it is a silent no-op. MySQL is not merely unimplemented but unimplementable: SET TRANSACTION there applies to the next transaction and errors inside a started one. Worth knowing if you develop on SQLite and deploy to Postgres — the constraint appears in production for the first time. (DD-027)
  • PropagationMode.NESTED needs savepoints. The adapter checks TypeORM's own driver.transactionSupport flag and throws IllegalTransactionStateError naming the driver and the alternatives, instead of running your "nested" transaction as part of the outer one.

Multiple dataSources

One forRoot call per dataSource:

TypeOrmTransactionalModule.forRoot({ isDefault: true }),        // 'default'
TypeOrmTransactionalModule.forRoot({ dataSource: 'billing' }),  // 'billing'
@Transactional({ dataSource: 'billing' })
async chargeCard() {
  return this.invoiceRepo.save(/* ... */); // repo bound to 'billing'
}

A repository bound to dataSource A, used inside a @Transactional({ dataSource: 'B' }) method, autocommits: its patched manager looks for an active transaction on A, finds none, and falls back to its original manager. Distributed transactions across dataSources are not supported — that is deliberate, and cross-dataSource atomicity is what the outbox is for.

Async configuration

TypeOrmTransactionalModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (cfg: ConfigService) => ({
    dataSource: cfg.get('DATA_SOURCE_NAME', 'default'),
    isDefault: true,
  }),
});

Registration is deferred to OnModuleInit so the dataSource resolves correctly even when paired with TypeOrmModule.forRootAsync. Per-dataSource adapter tokens are not registered on this path, because NestJS needs provider tokens at module-definition time; use sync forRoot({ dataSource }) if you inject adapters by token.

Compatibility

| Peer | Supported range | | --- | --- | | Node.js | >=22.13.0 | | typeorm | ^0.3.0 \|\| ^1.0.0 | | @nestjs/typeorm | ^10.0.0 \|\| ^11.0.0 \|\| ^12.0.0 | | @nestjs/common / @nestjs/core | ^10.0.0 \|\| ^11.0.0 \|\| ^12.0.0 | | reflect-metadata | ^0.1.13 \|\| ^0.2.0 | | rxjs | ^7.0.0 |

Both stable TypeORM lines are supported. CI runs the full unit and integration matrix — including savepoints and isolation against a real Postgres — at three points of that range: 0.3.31, 1.0.0 and 1.1.0.

Testing

For unit tests, TypeORM's in-memory sqljs driver is enough:

const ds = new DataSource({ type: 'sqljs', synchronize: true, entities: [Order] });
await ds.initialize();
const adapter = new TypeOrmTransactionAdapter(ds, 'default');

Note that readOnly is not enforced on sqljs, so a read-only violation your tests miss can still surface on Postgres. For tests that need real dialect behaviour, run Postgres through testcontainers; the testing-patterns example shows both layers.

Documentation

License

MIT