{"_id":"@bro-ankit/nestjs-typeorm-transactional-context","_rev":"2-9a5f8d3a73c5842dfb2f5d2265f0aa07","name":"@bro-ankit/nestjs-typeorm-transactional-context","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.1":{"name":"@bro-ankit/nestjs-typeorm-transactional-context","version":"1.0.1","keywords":["nestjs","typeorm","transaction","database","postgres","mysql","decorator","async-context","transactional","orm"],"author":{"name":"Ankit Pradhan"},"license":"MIT","_id":"@bro-ankit/nestjs-typeorm-transactional-context@1.0.1","maintainers":[{"name":"ankit012","email":"jsankit99@gmail.com"}],"homepage":"https://github.com/bro-ankit/nestjs-typeorm-transactional-context#readme","bugs":{"url":"https://github.com/bro-ankit/nestjs-typeorm-transactional-context/issues"},"dist":{"shasum":"452b52a566657aff3891430e119708b67036ff38","tarball":"https://registry.npmjs.org/@bro-ankit/nestjs-typeorm-transactional-context/-/nestjs-typeorm-transactional-context-1.0.1.tgz","fileCount":27,"integrity":"sha512-K/FyL1x+ozxih49rd7FW/8jyFFlremCXIchedVae6dRvuixXJsYvnNDiufnutK3cabfI+9RQJC0Vh8ZVvTRjDA==","signatures":[{"sig":"MEYCIQDXBaqq8yKNJuAaLW31R8FC4xcuVXRXFQH53cOCljnM2AIhAOrPfCadEYVAogsNMpk6F1XoLZ6Yzr5lTPlAEXdoPAeW","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":38748},"main":"dist/index.js","_from":"file:bro-ankit-nestjs-typeorm-transactional-context-1.0.1.tgz","types":"dist/index.d.ts","engines":{"node":">=18"},"scripts":{"lint":"eslint ./","test":"vitest run","build":"pnpm run clean && tsc -p tsconfig.build.json","clean":"rimraf dist","test:cov":"vitest run --coverage"},"_npmUser":{"name":"ankit012","email":"jsankit99@gmail.com"},"_resolved":"/private/var/folders/gt/bykgykf93qzft4vsc5lqmm980000gn/T/8bcf4865f742fbdc345fa82a2134055e/bro-ankit-nestjs-typeorm-transactional-context-1.0.1.tgz","_integrity":"sha512-K/FyL1x+ozxih49rd7FW/8jyFFlremCXIchedVae6dRvuixXJsYvnNDiufnutK3cabfI+9RQJC0Vh8ZVvTRjDA==","repository":{"url":"git+https://github.com/bro-ankit/nestjs-typeorm-transactional-context.git","type":"git"},"_npmVersion":"10.9.0","description":"> Transaction management for NestJS with TypeORM supporting propagation and isolation.","directories":{},"lint-staged":{"*.{ts,js,json,md}":["prettier --write","eslint --fix"]},"_nodeVersion":"20.15.0","dependencies":{"reflect-metadata":"^0.1.13"},"_hasShrinkwrap":false,"devDependencies":{"pg":"^8.17.2","husky":"^8.0.0","eslint":"^9.39.2","rimraf":"^6.1.2","vitest":"^2.0.2","ts-node":"^10.9.2","typeorm":"^0.3.17","prettier":"^3.8.1","@swc/core":"^1.15.11","typescript":"^5.3.0","@types/node":"^25.0.10","lint-staged":"^16.2.7","@nestjs/core":"^10.0.0","unplugin-swc":"^1.5.9","@nestjs/common":"^10.0.0","@nestjs/testing":"^10.0.0","@nestjs/typeorm":"^11.0.0","@vitest/coverage-v8":"2.1.9","eslint-plugin-import":"^2.32.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","@typescript-eslint/parser":"^8.54.0","@testcontainers/postgresql":"^11.11.0","@typescript-eslint/eslint-plugin":"^8.54.0","eslint-import-resolver-typescript":"^4.4.4"},"peerDependencies":{"typeorm":"^0.3","@nestjs/core":"^9 || ^10","@nestjs/common":"^9 || ^10"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-typeorm-transactional-context_1.0.1_1769576283351_0.7383211350810568","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@bro-ankit/nestjs-typeorm-transactional-context","version":"1.0.2","description":"> Transaction management for NestJS with TypeORM supporting propagation and isolation.","main":"dist/index.js","types":"dist/index.d.ts","repository":{"type":"git","url":"git+https://github.com/bro-ankit/nestjs-typeorm-transactional-context.git"},"scripts":{"prepare":"husky install","test":"vitest run","lint":"eslint ./","test:cov":"vitest run --coverage","clean":"rimraf dist","build":"pnpm run clean && tsc -p tsconfig.build.json","prepublishOnly":"pnpm test && pnpm build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@nestjs/common":"^9 || ^10","@nestjs/core":"^9 || ^10","typeorm":"^0.3"},"devDependencies":{"@nestjs/common":"^10.0.0","@nestjs/core":"^10.0.0","@nestjs/testing":"^10.0.0","@nestjs/typeorm":"^11.0.0","@swc/core":"^1.15.11","@testcontainers/postgresql":"^11.11.0","@types/node":"^25.0.10","@typescript-eslint/eslint-plugin":"^8.54.0","@typescript-eslint/parser":"^8.54.0","@vitest/coverage-v8":"2.1.9","eslint":"^9.39.2","eslint-config-prettier":"^10.1.8","eslint-import-resolver-typescript":"^4.4.4","eslint-plugin-import":"^2.32.0","eslint-plugin-prettier":"^5.5.5","husky":"^8.0.0","lint-staged":"^16.2.7","pg":"^8.17.2","prettier":"^3.8.1","rimraf":"^6.1.2","ts-node":"^10.9.2","typeorm":"^0.3.17","typescript":"^5.3.0","unplugin-swc":"^1.5.9","vitest":"^2.0.2"},"lint-staged":{"*.{ts,js,json,md}":["prettier --write","eslint --fix"]},"engines":{"node":">=18"},"keywords":["nestjs","typeorm","transaction","database","postgres","mysql","decorator","async-context","transactional","orm"],"author":{"name":"Ankit Pradhan"},"license":"MIT","packageManager":"pnpm@10.28.1","_id":"@bro-ankit/nestjs-typeorm-transactional-context@1.0.2","gitHead":"58c828742cdf1c568d3d632928c4a74900b10aa6","bugs":{"url":"https://github.com/bro-ankit/nestjs-typeorm-transactional-context/issues"},"homepage":"https://github.com/bro-ankit/nestjs-typeorm-transactional-context#readme","_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-YcGymdEVUMpPc2+5mVL7rJ5kENkNtOYlDF1ZDnPQmxRfwOVsYb6zMVPQ+Y/FSTDCWXEKeZW5SIHomsCbRnGHbw==","shasum":"f9924d068d74c40ae5a3a5b99324096a893a3994","tarball":"https://registry.npmjs.org/@bro-ankit/nestjs-typeorm-transactional-context/-/nestjs-typeorm-transactional-context-1.0.2.tgz","fileCount":27,"unpackedSize":40470,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDxboKqUpcUy5lBPH2mTsPiDgQUVpYRVC4ndzK/hAjZRAIhAN5jesM7eL6xtSS1WUkBTcK89YoOUlmldQNJOgx4FPhx"}]},"_npmUser":{"name":"ankit012","email":"jsankit99@gmail.com"},"directories":{},"maintainers":[{"name":"ankit012","email":"jsankit99@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nestjs-typeorm-transactional-context_1.0.2_1783152327290_0.498069107259826"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-28T04:58:03.261Z","modified":"2026-07-04T08:05:27.579Z","1.0.1":"2026-01-28T04:58:03.531Z","1.0.2":"2026-07-04T08:05:27.431Z"},"bugs":{"url":"https://github.com/bro-ankit/nestjs-typeorm-transactional-context/issues"},"author":{"name":"Ankit Pradhan"},"license":"MIT","homepage":"https://github.com/bro-ankit/nestjs-typeorm-transactional-context#readme","keywords":["nestjs","typeorm","transaction","database","postgres","mysql","decorator","async-context","transactional","orm"],"repository":{"type":"git","url":"git+https://github.com/bro-ankit/nestjs-typeorm-transactional-context.git"},"description":"> Transaction management for NestJS with TypeORM supporting propagation and isolation.","maintainers":[{"name":"ankit012","email":"jsankit99@gmail.com"}],"readme":"# NestJS TypeORM Transactional Context\n\n> Transaction management for NestJS with TypeORM supporting propagation and isolation.\n\n[![npm version](https://img.shields.io/npm/v/@bro-ankit/nestjs-typeorm-transactional-context.svg)](https://www.npmjs.com/package/@bro-ankit/nestjs-typeorm-transactional-context)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Why This Package?\n\nManaging transactions in NestJS can be cumbersome. This library provides:\n\n- **Zero boilerplate** - Just add `@Transactional()` decorator\n- **Framework integration** - Built specifically for NestJS dependency injection\n- **Type safety** - Full TypeScript support with proper types\n- **Tested** - Battle-tested with comprehensive test coverage\n- **Lightweight** - Minimal dependencies, leverages existing TypeORM\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Usage Examples](#usage-examples)\n- [API Reference](#api-reference)\n- [Advanced Patterns](#advanced-patterns)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n\n---\n\n## Features\n\n- ✅ **Declarative Transactions** - Simple `@Transactional()` decorator\n- ✅ **Transaction Propagation** - Control nested transaction behavior\n- ✅ **Transaction-Aware Repositories** - Repositories that respect transaction context\n- ✅ **Isolation Levels** - Support for all TypeORM isolation levels\n- ✅ **Async Context** - Works with NestJS's async context\n- ✅ **TypeScript** - Full type safety\n- ✅ **Testing** - Easy to test with dependency injection\n\n---\n\n## Installation\n\n```bash\nnpm install @bro-ankit/nestjs-transactional-context\n# or\nyarn add @bro-ankit/nestjs-transactional-context\n# or\npnpm add @bro-ankit/nestjs-transactional-context\n```\n\n---\n\n## Requirements\n\n- NestJS 10.x or higher\n- TypeORM 0.3.x or higher\n- Node.js 18.x or higher\n\n---\n\n## Quick Start\n\n### 1. Import Modules\n\n```ts\nimport { Module } from \"@nestjs/common\";\nimport { TypeOrmModule } from \"@nestjs/typeorm\";\nimport { TransactionalModule } from \"@bro-ankit/nestjs-transactional-context\";\n\n@Module({\n  imports: [\n    TypeOrmModule.forRoot({\n      type: \"postgres\",\n      // ... your database config\n    }),\n    TransactionalModule, // Required\n  ],\n})\nexport class AppModule {}\n```\n\n### 2. Use the `@Transactional()` Decorator\n\n```ts\nimport { Injectable } from \"@nestjs/common\";\nimport { Repository } from \"typeorm\";\nimport { Transactional } from \"@bro-ankit/nestjs-transactional-context\";\nimport { User } from \"./user.entity\";\nimport { InjectRepository } from \"@nestjs/typeorm\";\n\n@Injectable()\nexport class UserService {\n  constructor(\n    @InjectRepository(User)\n    private readonly userRepository: Repository<User>,\n  ) {}\n\n  @Transactional()\n  async createUser(name: string, email: string): Promise<User> {\n    const user = this.userRepository.create({ name, email });\n    await this.userRepository.save(user);\n\n    if (!email.includes(\"@\")) {\n      throw new Error(\"Invalid email\"); // rolls back the created user\n    }\n\n    return user;\n  }\n}\n```\n\n### 3. Create Transaction-Aware Repositories\n\n```ts\nimport { Injectable } from \"@nestjs/common\";\nimport { Repository } from \"typeorm\";\nimport {\n  TransactionalAwareRepository,\n  DbTransactionContext,\n} from \"@bro-ankit/nestjs-transactional-context\";\nimport { User } from \"./user.entity\";\n\n@Injectable()\n@TransactionalAwareRepository(User)\nexport class UserRepository extends Repository<User> {\n  constructor(private readonly ctx: DbTransactionContext) {\n    super(User, ctx.getEntityManager());\n  }\n\n  async findByEmail(email: string): Promise<User | null> {\n    return this.findOne({ where: { email } });\n  }\n\n  async createUser(name: string, email: string): Promise<User> {\n    const user = this.create({ name, email });\n    return this.save(user);\n  }\n}\n```\n\nRegister in your module:\n\n```ts\nimport { Module } from \"@nestjs/common\";\nimport { TypeOrmModule } from \"@nestjs/typeorm\";\nimport { User } from \"./user.entity\";\nimport { UserRepository } from \"./user.repository\";\nimport { UserService } from \"./user.service\";\n\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  providers: [UserRepository, UserService],\n  exports: [UserService],\n})\nexport class UserModule {}\n```\n\n---\n\n## Usage Examples\n\n### Basic Transaction\n\n```ts\n@Injectable()\nexport class OrderService {\n  constructor(private readonly orderRepository: OrderRepository) {}\n\n  @Transactional()\n  async createOrder(userId: string, items: OrderItem[]): Promise<Order> {\n    const order = this.orderRepository.create({ userId, items });\n    await this.orderRepository.save(order);\n    return order; // Automatically commits\n  }\n}\n```\n\n### Nested Transactions with Propagation\n\n```ts\n@Injectable()\nexport class PaymentService {\n  constructor(\n    private readonly orderRepository: Repository<Order>,\n    private readonly paymentRepository: Repository<Payment>,\n    private readonly dbTransactionService: DbTransactionService,\n  ) {}\n\n  @Transactional()\n  async processPayment(orderId: string): Promise<Payment> {\n    const order = await this.orderRepository.findOne({\n      where: { id: orderId },\n    });\n    const payment = await this.createPaymentRecord(order);\n\n    // Independent transaction for logging\n    await this.dbTransactionService.executeInTransaction(\n      { propagation: false },\n      async () => {\n        await this.logPaymentAttempt(orderId);\n      },\n    );\n\n    return payment;\n  }\n\n  @Transactional()\n  private async createPaymentRecord(order: Order): Promise<Payment> {\n    return this.paymentRepository.save({ orderId: order.id } as Payment);\n  }\n\n  private async logPaymentAttempt(orderId: string): Promise<void> {\n    // logging logic here\n  }\n}\n```\n\n### Manual Transaction Control\n\n```ts\n@Injectable()\nexport class ReportService {\n  constructor(\n    private readonly dbTransactionService: DbTransactionService,\n    private readonly orderRepository: OrderRepository,\n    private readonly reportRepository: ReportRepository,\n  ) {}\n\n  async generateReport(): Promise<Report> {\n    return this.dbTransactionService.executeInTransaction(async () => {\n      const data = await this.orderRepository.find();\n      const processed = await this.processData(data);\n      return this.reportRepository.save(processed);\n    });\n  }\n}\n```\n\n### Transaction Rollback\n\n```ts\n@Injectable()\nexport class AccountService {\n  constructor(private readonly accountRepository: AccountRepository) {}\n\n  @Transactional()\n  async transfer(fromId: string, toId: string, amount: number): Promise<void> {\n    const fromAccount = await this.accountRepository.findOne({\n      where: { id: fromId },\n    });\n    const toAccount = await this.accountRepository.findOne({\n      where: { id: toId },\n    });\n\n    if (!fromAccount || !toAccount) throw new Error(\"Accounts not found\");\n\n    if (fromAccount.balance < amount) {\n      throw new Error(\"Insufficient funds\"); // Automatically rolls back\n    }\n\n    fromAccount.balance -= amount;\n    toAccount.balance += amount;\n\n    await this.accountRepository.save([fromAccount, toAccount]);\n  }\n}\n```\n\n### Isolation Levels\n\n```ts\nimport { IsolationLevel } from \"typeorm/driver/types/IsolationLevel\";\n\n@Injectable()\nexport class InventoryService {\n  constructor(\n    private readonly productRepository: Repository<Product>,\n    private readonly dbTransactionService: DbTransactionService,\n  ) {}\n\n  async updateStock(productId: string, quantity: number): Promise<void> {\n    await this.dbTransactionService.executeInTransaction(\n      { isolationLevel: \"SERIALIZABLE\" },\n      async () => {\n        const product = await this.productRepository.findOne({\n          where: { id: productId },\n        });\n        product.stock -= quantity;\n        await this.productRepository.save(product);\n      },\n    );\n  }\n}\n```\n\n---\n\n## API Reference\n\n### `@Transactional()`\n\n```ts\n@Transactional()\nasync myMethod(): Promise<void> {\n  // commits automatically on success\n  // rolls back automatically on error\n}\n```\n\n### `@TransactionalAwareRepository(entity)`\n\n```ts\n@Injectable()\n@TransactionalAwareRepository(User)\nexport class UserRepository extends Repository<User> {\n  constructor(private readonly ctx: DbTransactionContext) {\n    super(User, ctx.getEntityManager());\n  }\n}\n```\n\n### `DbTransactionService`\n\n```ts\nexecuteInTransaction<T>(callback: () => Promise<T>): Promise<T>\nexecuteInTransaction<T>(options: TransactionOptions, callback: () => Promise<T>): Promise<T>\n```\n\n### `DbTransactionContext`\n\n```ts\ngetEntityManager(): EntityManager\ngetQueryRunner(): QueryRunner | undefined\nisActive(): boolean\ngetTransactionId(): string | undefined\n```\n\n### `TransactionOptions`\n\n```ts\ninterface TransactionOptions {\n  propagation?: boolean; // default: true\n  isolationLevel?: IsolationLevel; // default: 'READ COMMITTED'\n}\n```\n\n### `IsolationLevel`\n\n```ts\ntype IsolationLevel =\n  | \"READ UNCOMMITTED\"\n  | \"READ COMMITTED\"\n  | \"REPEATABLE READ\"\n  | \"SERIALIZABLE\";\n```\n\n---\n\n## Advanced Patterns\n\n### Testing with Transactions\n\n```ts\ndescribe(\"UserService\", () => {\n  let service: UserService;\n  let module: TestingModule;\n\n  beforeEach(async () => {\n    module = await Test.createTestingModule({\n      imports: [\n        TypeOrmModule.forRoot({\n          /* test db config */\n        }),\n        TransactionalModule,\n        TypeOrmModule.forFeature([User]),\n      ],\n      providers: [UserService, UserRepository],\n    }).compile();\n\n    service = module.get(UserService);\n  });\n\n  it(\"should rollback on error\", async () => {\n    await expect(service.createUser(\"test\", \"invalid-email\")).rejects.toThrow(\n      \"Invalid email\",\n    );\n\n    const count = await service.countUsers();\n    expect(count).toBe(0);\n  });\n});\n```\n\n### Concurrent Transactions\n\n```ts\nasync processBatch(items: Item[]): Promise<void> {\n  await Promise.all(\n    items.map(item =>\n      this.dbTransactionService.executeInTransaction({ propagation: false }, async () => {\n        await this.processItem(item);\n      }),\n    ),\n  );\n}\n```\n\n### Event-Driven / Background Jobs (SQS, Cron, Microservices)\n\nUnlike other libraries, this package doesn't depend on the HTTP Request object. It works anywhere thanks to the NestJS Discovery Module.\n\n```ts\n@Injectable()\nexport class SqsConsumer {\n  constructor(private readonly userService: UserService) {}\n\n  @SqsMessageHandler(\"user-queue\")\n  @Transactional() // Just works! No manual wrapping needed.\n  async handleMessage(message: AWS.SQS.Message) {\n    const data = JSON.parse(message.Body);\n    await this.userService.update(data.id, data.updates);\n  }\n}\n```\n\n---\n\n## Troubleshooting\n\n- Ensure `@Transactional()` is applied.\n- Ensure repository extends `@TransactionalAwareRepository`.\n- Ensure errors are thrown for rollback.\n\n---\n\n## Best Practices\n\n- Keep transactions short.\n- Don’t catch errors inside `@Transactional()`.\n- Use propagation wisely.\n- Set timeouts.\n- Use appropriate isolation levels.\n\n---\n\n## Comparison with Other Solutions\n\n| Feature                | This Package                        | nestjs-cls (Transactional)                          | Manual TypeORM | typeorm-transactional |\n| :--------------------- | :---------------------------------- | :-------------------------------------------------- | :------------- | :-------------------- |\n| **NestJS Integration** | ✅ Native (Simple Import)           | ✅ Plugin-based                                     | ⚠️ Manual      | ⚠️ Limited            |\n| **Setup Complexity**   | ✅ **Minimal (Plug-and-play)**      | ⚠️ High (Multi-step config)                         | ⚠️ Moderate    | ✅ Low                |\n| **Execution Context**  | ✅ **Agnostic (Works in SQS/Cron)** | ⚠️ (Controller Context Bound) Requires manual setup | ❌ Manual prop | ✅ Supports ALS       |\n| **Bundle Weight**      | ✅ **Ultralight (Single-purpose)**  | 📦 Heavy (Full CLS Suite)                           | N/A            | ⚠️ Moderate           |\n| **Decorator Support**  | ✅ `@Transactional()`               | ✅ `@Transactional()`                               | ❌ No          | ✅ `@Transactional()` |\n| **Async Context**      | ✅ Native `AsyncLocalStorage`       | ✅ `AsyncLocalStorage`                              | ❌ No          | ⚠️ Legacy             |\n| **Active Maintenance** | ✅ **Active**                       | ✅ Active                                           | N/A            | ❌ Archived           |\n| **TypeScript First**   | ✅                                  | ✅                                                  | ✅             | ⚠️                    |\n\n---\n\n## Contributing\n\nContributions are welcome! Submit a PR.\n\n---\n\n## License\n\nMIT © [Ankit Pradhan](https://github.com/bro-ankit)\n\n```\n\n```\n","readmeFilename":"README.md"}