npm: microspace-transaction-handler
A lightweight yet powerful transaction handler for MongoDB + Express, built with microservices architecture in mind.
It abstracts away session handling, coordination, and rollback logic so your services can run distributed transactions reliably.
- 🚀 MongoDB Transaction Support: Built-in support with automatic session handling
- 🔄 Express Middleware: Preconfigured endpoints for transaction control
- 🏗️ Microservices Ready: Designed for service isolation and inter-service coordination
- 🌐 Distributed Transactions: Saga and event-driven orchestration patterns supported
- ⏰ Auto-Expiration: Configurable TTL with automatic cleanup
- ⚙️ Environment Config: Customize TTL, limits, and session options via env vars
- 🛡️ Enhanced Errors: Specific error types with detailed information
- 🧪 Test Coverage: Comprehensive unit and integration tests
- 📝 TypeScript: Full type definitions and strict typing
Package on npm: microspace-transaction-handler
npm install microspace-transaction-handlerNote: This package includes Mongoose as a dependency, so you don't need to install it separately. Mongoose 7.0+ is automatically included with the package.
Managing distributed transactions across microservices with MongoDB is complex. This package abstracts away session handling, coordination, and error recovery so you can focus on business logic.
- Node.js 16+
- MongoDB 4.0+ (with replica set for transactions)
- Express.js 4.18+
- Mongoose 7.0+
This package is specifically designed for microservices environments and provides:
- Service Isolation: Each microservice can manage its own transactions independently
- Inter-Service Communication: HTTP endpoints for coordinating transactions across services
- Distributed Patterns: Support for Saga patterns and distributed transaction coordination
- Observability: Built-in logging and monitoring for distributed transaction tracking
Each service runs its own transaction handler. A coordination layer ensures distributed consistency using Saga/event-driven patterns.
┌───────────────────────────────────────────────────────────────┐
│ Microservices Architecture │
├───────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │User Service │ │Order Service│ │Payment Service│ │
│ │ │ │ │ │ │ │
│ │┌───────────┐│ │┌───────────┐│ │┌───────────┐│ │
│ ││Transaction││ ││Transaction││ ││Transaction││ │
│ ││Handler ││ ││Handler ││ ││Handler ││ │
│ │└───────────┘│ │└───────────┘│ │└───────────┘│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌───────────────────────┴─────────────────────────────────┐ │
│ │ Transaction Coordination Layer │ │
│ │ • Saga Pattern Orchestration │ │
│ │ • Event-Driven Coordination │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────┴────────────────────────────────┐ │
│ │ Data Layer │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │User DB │ │Order DB │ │Payment DB │ │ │
│ │ │(MongoDB) │ │(MongoDB) │ │(MongoDB) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
1. Service A creates transaction
┌─────────────┐
│Service A │───► Create Transaction
└─────────────┘
2. Service A performs operations
┌─────────────┐
│Service A │───► DB Operations (with session)
└─────────────┘
3. Service A notifies Service B
┌─────────────┐ HTTP Call ┌─────────────┐
│Service A │───────────────►│Service B │
└─────────────┘ └─────────────┘
4. Service B joins transaction
┌─────────────┐
│Service B │───► Join Transaction (via HTTP)
└─────────────┘
5. All services commit
┌─────────────┐ Commit ┌─────────────┐
│Service A │───────────────►│Service B │
└─────────────┘ └─────────────┘
import express from 'express';
import { createTransactionEndpoints } from '@microspace/transaction-handler';
const app = express();
app.use(express.json());
// Set up transaction endpoints
createTransactionEndpoints(app);
app.listen(3000, () => {
console.log('Server running on port 3000');
console.log('Transaction endpoints available at /transaction/*');
});That's it! Your Express server now has transaction management endpoints. See the API Reference for available endpoints.
import { TransactionContainer } from '@microspace/transaction-handler';
// Create a transaction
const transactionContainer = TransactionContainer.getInstance();
const transaction = await transactionContainer.createTransaction();
// Use the transaction in your operations
// ... your database operations here ...
// Commit the transaction
await transactionContainer.commitTransaction(transaction.id);// User Service
import { TransactionContainer, createTransactionEndpoints } from '@microspace/transaction-handler';
const userService = express();
createTransactionEndpoints(userService);
// Create a transaction for user operations
const transaction = await TransactionContainer.getInstance().createTransaction();
// User Service Operations
const user = await User.create(userData, { session: transaction.session });
const profile = await Profile.create(profileData, { session: transaction.session });
// Notify other services about the transaction
await notifyServices('user-created', { transactionId: transaction.id, userId: user.id });
// Commit when all services confirm
await TransactionContainer.getInstance().commitTransaction(transaction.id);// Order Service - Saga Orchestrator
class OrderSaga {
async createOrder(orderData: OrderData) {
const transaction = await TransactionContainer.getInstance().createTransaction();
try {
// Step 1: Create Order
const order = await this.createOrderInDB(orderData, transaction.session);
// Step 2: Reserve Inventory
await this.reserveInventory(order.items, transaction.id);
// Step 3: Process Payment
await this.processPayment(order.payment, transaction.id);
// Step 4: Send Notification
await this.sendOrderConfirmation(order, transaction.id);
// All steps successful - commit
await TransactionContainer.getInstance().commitTransaction(transaction.id);
} catch (error) {
// Rollback all operations
await this.compensateOrder(order, transaction.id);
await TransactionContainer.getInstance().rollbackTransaction(transaction.id);
throw error;
}
}
private async compensateOrder(order: Order, transactionId: string) {
// Compensation logic for each step
await this.cancelInventoryReservation(order.items, transactionId);
await this.refundPayment(order.payment, transactionId);
await this.sendCancellationNotification(order, transactionId);
}
}// Event-driven transaction coordination
class TransactionEventHandler {
async handleUserCreated(event: UserCreatedEvent) {
const transaction = await TransactionContainer.getInstance().createTransaction();
try {
// Create user profile
await Profile.create(event.userData, { session: transaction.session });
// Send welcome email
await this.emailService.sendWelcome(event.userData.email, transaction.id);
// Initialize user preferences
await this.preferencesService.initialize(event.userId, transaction.id);
// Commit
await TransactionContainer.getInstance().commitTransaction(transaction.id);
} catch (error) {
await TransactionContainer.getInstance().rollbackTransaction(transaction.id);
// Publish compensation event
await this.eventBus.publish('user-creation-failed', { userId: event.userId });
}
}
}The main class for managing transactions.
Returns the singleton instance of TransactionContainer.
Creates a new transaction with an auto-generated UUID.
ttl(optional): Time-to-live in milliseconds (default: 30000)
Retrieves a transaction by its ID.
Commits a transaction and removes it from the container.
Rolls back a transaction (undoes all changes) and removes it from the container.
Cancels a transaction (marks as aborted and rolls back all changes).
Returns all active transactions.
Clears all transactions (useful for testing).
The middleware creates the following endpoints:
Commits a transaction.
Response:
{
"message": "Transaction committed successfully",
"transactionId": "uuid-here"
}Rolls back a transaction (undoes all changes).
Response:
{
"message": "Transaction rolled back successfully",
"transactionId": "uuid-here"
}Cancels a transaction (marks as aborted and rolls back).
Response:
{
"message": "Transaction canceled successfully",
"transactionId": "uuid-here"
}Gets the status of a transaction.
Response:
{
"transactionId": "uuid-here",
"status": "pending",
"createdAt": 1234567890,
"ttl": 30000,
"canceled": false,
"operationsCount": 0
}interface Transaction {
id: string;
ttl: number;
createdAt: number;
status: 'pending' | 'committed' | 'rolledback' | 'expired';
canceled: boolean;
operations: TransactionOperation[];
session: ClientSession;
}type TransactionOperation = {
name: string;
commit: (session?: ClientSession) => Promise<void>;
rollback: (session?: ClientSession) => Promise<void>;
};import { Transaction, TransactionOperation } from '@microspace/transaction-handler';
const transaction = await transactionContainer.createTransaction();
// Add custom operations
const operation: TransactionOperation = {
name: 'user-creation',
commit: async (session) => {
// Your commit logic here
await User.create([userData], { session });
},
rollback: async (session) => {
// Your rollback logic here
await User.deleteOne({ _id: userData._id }, { session });
}
};
transaction.operations.push(operation);try {
const transaction = await transactionContainer.createTransaction();
// Your operations here
await transactionContainer.commitTransaction(transaction.id);
} catch (error) {
// Handle errors appropriately
if (transaction) {
await transactionContainer.rollbackTransaction(transaction.id);
}
throw error;
}LOG_LEVEL: Set the logging level (default: 'info')TRANSACTION_DEFAULT_TTL: Default TTL for transactions in milliseconds (default: 30000)TRANSACTION_CLEANUP_INTERVAL: Cleanup interval for expired transactions in milliseconds (default: 30000)TRANSACTION_MAX_CONCURRENT: Maximum number of concurrent transactions (default: 100)TRANSACTION_ENABLE_AUTO_CLEANUP: Enable automatic cleanup of expired transactions (default: true)
import { updateConfig, getConfig } from '@microspace/transaction-handler';
// Update configuration
updateConfig({
defaultTtl: 60000, // 60 seconds
maxConcurrentTransactions: 50,
enableAutoCleanup: true,
cleanupInterval: 30000
});
// Get current configuration
const config = getConfig();
console.log('Current TTL:', config.defaultTtl);Ensure your MongoDB instance is configured as a replica set for transaction support:
// MongoDB connection with transaction support
mongoose.connect('mongodb://localhost:27017/your-db', {
replicaSet: 'rs0' // Required for transactions
});# Run all tests
npm test
# Run unit tests only
npm run test:unit
# Run integration tests only (requires MongoDB)
npm run test:integration
# Run linting
npm run lint# Install dependencies
npm install
# Build the package
npm run buildThe package provides comprehensive error handling with specific error types:
| Error | Description |
|---|---|
TransactionNotFoundError |
Transaction ID not found |
TransactionExpiredError |
Transaction TTL exceeded |
TransactionCanceledError |
Attempt to commit canceled transaction |
MaxConcurrentTransactionsError |
Limit reached |
TransactionOperationError |
Operation commit/rollback failed |
MongoSessionError |
MongoDB session operations failed |
TransactionConfigError |
Invalid configuration |
TransactionValidationError |
Validation failures |
import {
TransactionNotFoundError,
isTransactionError,
getErrorCode
} from '@microspace/transaction-handler';
try {
await transactionContainer.commitTransaction('invalid-id');
} catch (error) {
if (isTransactionError(error)) {
console.log('Error code:', getErrorCode(error));
console.log('Transaction ID:', error.transactionId);
}
}The package automatically cleans up expired transactions:
- Configurable cleanup interval: Set how often to check for expired transactions
- Automatic rollback: Expired transactions are automatically rolled back (changes undone)
- Memory management: Prevents memory leaks from long-running transactions
- Logging: All cleanup operations are logged for monitoring
- Always handle errors: Wrap transaction operations in try-catch blocks
- Set appropriate TTL: Configure TTL based on your use case
- Monitor transactions: Use the status endpoint to monitor transaction health
- Clean up: Ensure transactions are properly committed or rolled back
- Test thoroughly: Use the provided test utilities for comprehensive testing
- Use environment variables: Configure service-specific settings via environment
- Implement health checks: Monitor service and transaction health
- Log comprehensively: Include transaction IDs in all log messages
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License: MIT (see LICENSE section below for details)
Copyright (c) 2025 Celestial
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For bugs and feature requests, please open an Issue. For private support, contact: [email protected]/[email protected]
We have exciting plans for future releases! Our goals include:
- Advanced observability - Metrics, tracing integration, and comprehensive monitoring dashboards
- Automatic Saga orchestration tools - Built-in patterns for complex distributed workflows
- Performance optimizations - Enhanced throughput and reduced latency
- Enhanced error handling - More granular error types and recovery strategies
- Additional middleware options - More Express integration patterns
- Support more databases beyond MongoDB - Extend to PostgreSQL, MySQL, and other popular databases
More releases coming soon! 🚀
Microspace is a collection of tools and libraries designed specifically for microservices architecture, providing developers with robust, production-ready solutions for building distributed systems.