@nest-omni/transaction
v1.0.1
Published
Transaction management library for NestJS + TypeORM with AsyncLocalStorage support
Downloads
245
Readme
@nest-omni/transaction
Transaction management library for NestJS + TypeORM with AsyncLocalStorage support.
Installation
npm install @nest-omni/transaction
npm install typeorm reflect-metadatayarn add @nest-omni/transaction
yarn add typeorm reflect-metadataQuick Start
1. Register Module
Register TransactionModule in your AppModule. It automatically discovers all TypeORM DataSources:
// app.module.ts
import { Module } from '@nestjs/common';
import { TransactionModule } from '@nest-omni/transaction';
@Module({
imports: [
TransactionModule.forRoot(),
// ... 其他模块
],
})
export class AppModule {}在入口文件导入 reflect-metadata:
// main.ts
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();2. Basic Usage
import { Injectable } from '@nestjs/common';
import { Transactional } from '@nest-omni/transaction';
import { User } from './user.entity';
import { DataSource } from 'typeorm';
@Injectable()
export class UserService {
constructor(
private readonly dataSource: DataSource,
) {}
@Transactional()
async createUser(name: string, email: string) {
// 使用 QueryRunner 自动管理事务
await this.dataSource.query(
'INSERT INTO user (name, email) VALUES (?, ?)',
[name, email]
);
// Repository 也在同一事务中
const user = this.dataSource.getRepository(User).create({ name, email });
await this.dataSource.getRepository(User).save(user);
}
}3. Transaction Propagation
@Injectable()
export class OrderService {
constructor(
private readonly userService: UserService,
private readonly productService: ProductService,
) {}
@Transactional()
async createOrder(userId: string, productId: string) {
// REQUIRED: 加入当前事务,如果没有则创建新事务
await this.userService.updateBalance(userId, -100);
// REQUIRES_NEW: 总是创建新事务
await this.productService.recordSale(productId);
// NESTED: 创建嵌套事务,回滚不影响父事务
await this.notifyUser(userId);
}
@Transactional({ propagation: Propagation.REQUIRES_NEW })
async recordSale(productId: string) {
// 独立事务
}
@Transactional({ propagation: Propagation.NESTED })
async notifyUser(userId: string) {
// 嵌套事务,回滚不会导致 createOrder 回滚
}
}Multiple DataSources
@Module({
imports: [
TransactionModule.forRoot(),
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
database: 'main_db',
}),
TypeOrmModule.forRoot({
name: 'log-db',
type: 'postgres',
host: 'localhost',
port: 5433,
database: 'log_db',
}),
],
})
export class AppModule {}指定数据源:
@Injectable()
export class LogService {
@Transactional({ connectionName: 'log-db' })
async writeLog(message: string) {
// 使用 log-db 数据源
}
}Transaction Hooks
Method 1: Using Listeners
import { Injectable } from '@nestjs/common';
import { TransactionEventListener } from '@nest-omni/transaction';
@Injectable()
export class OrderEventListener implements TransactionEventListener {
async onCommit(...args: unknown[]) {
// Execute after transaction commit
console.log('Order created successfully', args);
}
async onRollback(error: Error, ...args: unknown[]) {
// Execute after transaction rollback
console.error('Order creation failed', error);
}
}@Injectable()
export class OrderService {
@Transactional()
@TransactionalEventListeners(OrderEventListener)
async createOrder(data: OrderDto) {
// 业务逻辑
}
}Method 2: Using Hook Functions
import { runOnTransactionCommit, runOnTransactionRollback } from '@nest-omni/transaction';
@Injectable()
export class OrderService {
@Transactional()
async createOrder(data: OrderDto) {
// 业务逻辑...
// 提交后执行
runOnTransactionCommit(async () => {
await this.emailService.sendConfirmation();
});
// 回滚后执行
runOnTransactionRollback(async (error) => {
await this.loggingService.logError(error);
});
}
}Transaction Options
@Transactional({
// DataSource name
connectionName?: string;
// Isolation level
isolationLevel?: IsolationLevel;
// Propagation behavior
propagation?: Propagation;
})Propagation
| Value | Description |
|---|---|
| REQUIRED | Default. Join existing transaction, or create new if none |
| SUPPORTS | Join existing transaction, or execute non-transactional if none |
| NESTED | Nested transaction, rollback doesn't affect parent transaction |
| REQUIRES_NEW | Always create new transaction, suspend current transaction |
Isolation Level
| Value | Description |
|---|---|
| READ_UNCOMMITTED | Read uncommitted |
| READ_COMMITTED | Read committed (default) |
| REPEATABLE_READ | Repeatable read |
| SERIALIZABLE | Serializable |
Integration Testing
Use TestTransactionModule for automatic rollback in tests:
import { Test, TestingModule } from '@nestjs/testing';
import { TestTransactionModule, getTestQueryRunnerToken } from '@nest-omni/transaction';
import { DataSource } from 'typeorm';
import { QueryRunner } from 'typeorm';
describe('OrderService', () => {
let app: INestApplication;
let testQueryRunner: QueryRunner;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [AppModule, TestTransactionModule.forRoot()],
}).compile();
app = module.createNestApplication();
await app.init();
testQueryRunner = app.get<QueryRunner>(getTestQueryRunnerToken());
});
beforeEach(async () => {
await testQueryRunner.startTransaction();
});
afterEach(async () => {
await testQueryRunner.rollbackTransaction();
});
afterAll(async () => {
await app.close();
});
it('should create order', async () => {
// Test with automatic rollback
});
});API
// 装饰器
@Transactional(options?: TransactionOptions)
@TransactionalEventListeners(...listeners: Type<TransactionEventListener>[])
// 函数
runInTransaction(fn, options?)
wrapInTransaction(fn, options?)
runOnTransactionCommit(callback)
runOnTransactionRollback(callback)
// 类/接口
TransactionModule
TestTransactionModule
TransactionalError
NoRegisteredDataSourceError
NotRollBackError
TransactionEventListenerLicense
MIT
