Socket
Book a DemoInstallSign in
Socket

adonisjs-transaction-decorator

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

adonisjs-transaction-decorator

Transaction decorator and mixin for AdonisJS applications

latest
Source
npmnpm
Version
1.0.9
Version published
Maintainers
1
Created
Source

adonisjs-transaction-decorator

🚀 Advanced transaction management for AdonisJS applications

Powerful transaction decorator and mixin that provides automatic transaction handling with AsyncLocalStorage, retry mechanisms, debug logging, and comprehensive utilities.

Features

@transaction() decorator - Automatic transaction wrapping for methods
🔄 Nested transaction support - Reuses existing transactions
🔁 Retry mechanism - Configurable retry logic for failed transactions
📊 Debug logging - Transaction lifecycle logging with timing
Transactional mixin - Automatic transaction injection for Lucid models
🛠 Advanced utilities - Sequence, parallel, conditional execution
🔍 Transaction stats - Runtime statistics and monitoring
🎯 TypeScript first - Full type safety and IntelliSense support

Installation

npm install adonisjs-transaction-decorator

Quick Start

1. Transaction Decorator

import { transaction } from 'adonisjs-transaction-decorator'

class UserService {
  @transaction()
  async createUser(userData: any) {
    // This method automatically runs in a transaction
    const user = await User.create(userData)
    await Profile.create({ userId: user.id, ...profileData })
    return user
  }

  @transaction({ 
    debug: true, 
    retry: { attempts: 3, delay: 100 } 
  })
  async createUserWithRetry(userData: any) {
    // Automatic retry on failure with debug logging
    return await User.create(userData)
  }
}

2. Transactional Mixin

import { Transactional } from 'adonisjs-transaction-decorator'
import { BaseModel } from '@adonisjs/lucid/orm'

class User extends Transactional(BaseModel) {
  // All operations automatically use current transaction context
}

class UserController {
  @transaction()
  async store({ request }: HttpContext) {
    // All User operations will use the same transaction
    const user = await User.create(request.body())
    await User.query().where('active', false).update({ active: true })
    return user
  }
}

3. Programmatic Usage

import { runInTransaction } from 'adonisjs-transaction-decorator'

// Execute multiple operations in a transaction
const result = await runInTransaction(async () => {
  const user = await User.create({ name: 'John' })
  const profile = await Profile.create({ userId: user.id })
  return { user, profile }
}, { 
  debug: true,
  isolationLevel: 'repeatable read'
})

API Reference

Transaction Options

interface TransactionOptions {
  connection?: string              // Custom database connection
  isolationLevel?: IsolationLevel  // Transaction isolation level
  debug?: boolean                  // Enable debug logging
  timeout?: number                 // Transaction timeout (ms)
  retry?: {                        // Retry configuration
    attempts: number
    delay: number
  }
}

Core Functions

@transaction(options?: TransactionOptions)

Decorator that wraps methods in a transaction.

class OrderService {
  @transaction({ isolationLevel: 'serializable' })
  async processOrder(orderData: any) {
    // High isolation level for critical operations
  }

  @transaction({ 
    retry: { attempts: 3, delay: 200 },
    debug: true 
  })
  async processPayment(paymentData: any) {
    // Retry failed payments with logging
  }
}

runInTransaction<T>(fn, options?): Promise<T>

Execute a function within a transaction programmatically.

const order = await runInTransaction(async () => {
  const order = await Order.create(orderData)
  await OrderItem.createMany(items.map(item => ({ ...item, orderId: order.id })))
  await Inventory.decrement(items)
  return order
}, { debug: true })

getCurrentTransaction(): TransactionClientContract | undefined

Get the current transaction client.

const trx = getCurrentTransaction()
if (trx) {
  // Manual database operations with current transaction
  await trx.raw('UPDATE counters SET value = value + 1')
}

isInTransaction(): boolean

Check if currently executing within a transaction.

if (isInTransaction()) {
  console.log('Running in transaction context')
}

getTransactionStats()

Get current transaction statistics.

const stats = getTransactionStats()
console.log(`Transaction ${stats.id} running for ${stats.duration}ms`)

Transactional Mixin

The Transactional mixin automatically injects the current transaction into all Lucid model operations:

class User extends Transactional(BaseModel) {
  // All static and instance methods automatically use current transaction
}

// Usage
@transaction()
async createUserWithPosts() {
  const user = await User.create({ name: 'John' })        // Uses transaction
  const post = await Post.create({ userId: user.id })     // Uses transaction
  await user.save()                                       // Uses transaction
  return user
}

Transaction Utilities

TransactionUtils.sequence<T>(operations, options?): Promise<T[]>

Execute operations sequentially within a transaction.

const results = await TransactionUtils.sequence([
  () => User.create({ name: 'Alice' }),
  () => User.create({ name: 'Bob' }),
  () => User.create({ name: 'Charlie' })
], { debug: true })

TransactionUtils.parallel<T>(operations, options?): Promise<T[]>

Execute operations in parallel within a transaction.

const [user, profile, settings] = await TransactionUtils.parallel([
  () => User.create(userData),
  () => Profile.create(profileData),
  () => Settings.create(settingsData)
])

TransactionUtils.conditional<T>(condition, operation, options?)

Conditionally execute operation in transaction.

const user = await TransactionUtils.conditional(
  () => shouldCreateUser,
  () => User.create(userData),
  { debug: true }
)

TransactionUtils.withSavepoint<T>(name, operation)

Execute operation with savepoint support (database dependent).

await runInTransaction(async () => {
  await User.create(userData)
  
  await TransactionUtils.withSavepoint('checkpoint', async () => {
    // This can be rolled back to savepoint on error
    await Profile.create(profileData)
  })
})

Advanced Examples

Error Handling with Retry

class PaymentService {
  @transaction({ 
    retry: { attempts: 3, delay: 1000 },
    debug: true 
  })
  async processPayment(amount: number, cardToken: string) {
    // Automatically retries on failure
    const charge = await this.chargeCard(cardToken, amount)
    await Transaction.create({ amount, status: 'completed' })
    await this.sendReceipt(charge.receiptEmail)
    return charge
  }
}

Complex Business Logic

class OrderService {
  @transaction({ isolationLevel: 'serializable' })
  async fulfillOrder(orderId: number) {
    const order = await Order.findOrFail(orderId)
    
    // All operations use the same transaction
    for (const item of order.items) {
      await Inventory.decrement(item.productId, item.quantity)
      await ShippingLabel.create({ orderId, itemId: item.id })
    }
    
    await order.merge({ status: 'fulfilled' }).save()
    await this.notifyCustomer(order.customerId)
    
    return order
  }
}

Multiple Database Connections

class DataMigrationService {
  @transaction({ connection: 'analytics', debug: true })
  async migrateUserData(userId: number) {
    // Uses 'analytics' database connection
    const user = await User.create(userData)
    await UserAnalytics.create({ userId: user.id })
    return user
  }
}

Monitoring and Debugging

class ReportService {
  @transaction({ debug: true })
  async generateReport() {
    const stats = getTransactionStats()
    console.log(`Report generation started: ${stats.id}`)
    
    // Complex report generation...
    await this.aggregateData()
    await this.generateCharts()
    await this.saveReport()
    
    const finalStats = getTransactionStats()
    console.log(`Report completed in ${finalStats.duration}ms`)
  }
}

Best Practices

1. Use Appropriate Isolation Levels

// For critical operations requiring consistency
@transaction({ isolationLevel: 'serializable' })
async transferMoney(fromAccount: number, toAccount: number, amount: number) {
  // Prevents phantom reads and ensures data consistency
}

// For most operations (default)
@transaction({ isolationLevel: 'read committed' })
async updateProfile(userId: number, data: any) {
  // Good balance of consistency and performance
}

2. Enable Debug Logging in Development

// In development
@transaction({ debug: true })

// In production
@transaction({ debug: false })

3. Use Retry for Network-Dependent Operations

@transaction({ 
  retry: { attempts: 3, delay: 500 } 
})
async syncWithExternalAPI() {
  // Handles temporary network issues
}

4. Combine with Lucid Model Events

class User extends Transactional(BaseModel) {
  @beforeCreate()
  static async hashPassword(user: User) {
    // This will use the current transaction context
    user.password = await Hash.make(user.password)
  }
}

Compatibility

  • AdonisJS: ^6.0.0
  • Node.js: >=18.0.0
  • TypeScript: ^5.0.0
  • Databases: PostgreSQL, MySQL, SQLite, MSSQL (via Lucid ORM)

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

MIT License - see LICENSE file for details.

Support

Keywords

adonisjs

FAQs

Package last updated on 30 Jun 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts