{"_id":"@deju_coder/transactional-outbox","name":"@deju_coder/transactional-outbox","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@deju_coder/transactional-outbox","version":"1.0.0","description":"A clean, dependency-injected Node.js implementation of the Transactional Outbox pattern","main":"src/index.js","scripts":{"test":"jest","start:example":"node examples/basic.js"},"repository":{"type":"git","url":"git+https://github.com/tejes49/TransactionalOutbox.git"},"keywords":["outbox","transactional-outbox","microservices","events","pg","postgres","kafka","idempotency"],"author":{"name":"tejes49"},"license":"MIT","bugs":{"url":"https://github.com/tejes49/TransactionalOutbox/issues"},"homepage":"https://github.com/tejes49/TransactionalOutbox#readme","dependencies":{"pg":"^8.11.3","uuid":"^9.0.1","kafkajs":"^2.2.4"},"devDependencies":{"jest":"^29.7.0"},"_id":"@deju_coder/transactional-outbox@1.0.0","gitHead":"f401a88413a4927bb9224018bc8777e7edab8d52","_nodeVersion":"22.17.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-FQXaD3a50N44jgRkZ4fCG8ZKQkERoCA6rTV+OO6qjMDKSjw9SVyITPnRn6H4Ok205MRYD2fNC8+INTqqWqk5KA==","shasum":"c925e07b1d2933300f6b6c77242f413ea6df478a","tarball":"https://registry.npmjs.org/@deju_coder/transactional-outbox/-/transactional-outbox-1.0.0.tgz","fileCount":9,"unpackedSize":18334,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCbEWS/++08xv8XmxGx6IhL84JKxDsi2Dvfe0wjdn55AAIgCJAzBCn2mpUzjNDeZMVmBJQUiUtLZUgTecdmcIFHK84="}]},"_npmUser":{"name":"deju_coder","email":"dejeswars@gmail.com"},"directories":{},"maintainers":[{"name":"deju_coder","email":"dejeswars@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/transactional-outbox_1.0.0_1775532870953_0.4293529445448474"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-07T03:34:30.868Z","1.0.0":"2026-04-07T03:34:31.119Z","modified":"2026-04-07T03:34:31.298Z"},"maintainers":[{"name":"deju_coder","email":"dejeswars@gmail.com"}],"description":"A clean, dependency-injected Node.js implementation of the Transactional Outbox pattern","homepage":"https://github.com/tejes49/TransactionalOutbox#readme","keywords":["outbox","transactional-outbox","microservices","events","pg","postgres","kafka","idempotency"],"repository":{"type":"git","url":"git+https://github.com/tejes49/TransactionalOutbox.git"},"author":{"name":"tejes49"},"bugs":{"url":"https://github.com/tejes49/TransactionalOutbox/issues"},"license":"MIT","readme":"# Transactional Outbox\n\nA robust, dependency-injected Node.js implementation of the Transactional Outbox pattern for PostgreSQL.\n\n## ⚠️ The Problem: \"Dual Writes\"\n\nIn distributed systems and microservices, a service often needs to update its own local database _and_ simultaneously notify other services by publishing an event to a message broker (like Kafka or RabbitMQ). \n\nIf you try to perform these two actions sequentially without a distributed transaction coordinator, you encounter the **Dual Write Problem**:\n1. If the database commit succeeds but the network call to Kafka fails, downstream services are never notified.\n2. If Kafka succeeds but the database commit fails, downstream services react to \"phantom\" data that was rolled back.\n\n## 💡 The Solution: Transactional Outbox\n\nThe Transactional Outbox pattern solves this by using your primary database as the *single source of truth*. Instead of sending the message to the broker directly, you insert the event into an `outbox_events` table within the **exact same database transaction** as your domain logic. A background worker then reliably relays those events to your message broker.\n\n### Architecture Diagram\n\n```text\n  ┌───────────────────────┐\n  │                       │ 1. start transaction\n  │   Node.js Service     ├──────────┐\n  │                       │          │\n  └──────────┬────────────┘          │ 2. insert User\n             │ 3. insert Event       │\n             ▼                       ▼\n ┌──────────────────────────────────────┐                   ┌────────────────┐\n │  PostgreSQL Database                 │ 4. commit()       │                │\n │ ┌────────────┐ ┌───────────────────┐ │                   │                │\n │ │ users      │ │ outbox_events     │ │◄──────────────────┤  OutboxWorker  │\n │ └────────────┘ └─────────┬─────────┘ │ 5. poll pending   │                │\n └──────────────────────────┼───────────┘                   └────────┬───────┘\n                            │                                        │\n                            │                                        │ 6. publish()\n                            │                                        ▼\n                            │                               ┌────────────────┐\n                            │ 7. mark done()                │ Message Broker │\n                            └───────────────────────────────┤ (e.g., Kafka)  │\n                                                            └────────────────┘\n```\n\n## 📦 Installation\n\n```bash\nnpm install transactional-outbox pg uuid\n```\n*(Note: `pg` and `uuid` are required peer/internal dependencies)*\n\n## 🚀 Quick Start & Usage Example\n\n### 1. Database Schema\nFirst, apply the `schema.sql` (found in the root of the repository) to your PostgreSQL database. This creates the required `outbox_events` table and the optimized `idx_outbox_events_pending` index for high-speed polling.\n\n### 2. Service Integration\n\nHere is a complete example of injecting the outbox into your transaction and starting the relay worker:\n\n```javascript\nconst { Pool } = require('pg');\nconst { \n  Outbox, \n  EventStore, \n  OutboxWorker, \n  KafkaPublisher \n} = require('transactional-outbox');\n\n// 1. Initialize your PostgreSQL connection pool\nconst pool = new Pool({ connectionString: 'postgres://localhost/mydb' });\n\n// 2. Setup the Outbox interceptor\nconst outbox = new Outbox(pool);\n\n// 3. Setup the background Worker and your Broker Publisher\nconst eventStore = new EventStore(pool);\nconst publisher = new KafkaPublisher({ \n  clientId: 'my-service', \n  brokers: ['localhost:9092'] \n});\n\nasync function main() {\n  // Connect cleanly to your message broker first\n  await publisher.connect();\n  \n  // Start the background relay worker (polls every 2000ms by default)\n  const worker = new OutboxWorker(eventStore, publisher);\n  worker.start(2000);\n\n  // ---------------------------------------------------------\n  // 4. Safely wrap your domain logic and event in ONE transaction\n  // ---------------------------------------------------------\n  await outbox.publishTx(async (tx) => {\n    \n    // Standard domain mutation using the provided `tx` client\n    const res = await tx.query(\n      'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id', \n      ['Alice', 'alice@test.com']\n    );\n    \n    // Stage the event payload using the injected outbox helper\n    await tx.outbox.publish('user.created', { \n      id: res.rows[0].id, \n      name: 'Alice' \n    });\n    \n  }); // <-- Automatically COMMITs both the user and the event!\n  \n  console.log('User created and event safely queued for background relay!');\n}\n\nmain();\n```\n\n## ⚙️ Core Features\n\n*   **Atomic Dual-Writes**: Wraps your queries in a safe `BEGIN`/`COMMIT` block automatically.\n*   **Concurrent-Safe Polling**: Implements PostgreSQL's highly-optimized `FOR UPDATE SKIP LOCKED` query so multiple Node.js workers can poll the table simultaneously without database lock contention.\n*   **At-Least-Once Delivery**: Events are only marked as `done` *after* the broker successfully receives them.\n*   **Exponential Backoff for Failures**: Dead message broker? No problem. Built-in exponential backoff automatically schedules failing events for the future and takes permanently dead messages out of the loop after 5 retries.\n*   **Pluggable Message Brokers**: Ships with a ready-to-use `KafkaPublisher` and `ConsolePublisher`, but you can easily write your own RabbitMQ or SNS publisher as long as it exposes a simple `publish(topic, payload)` method.\n","readmeFilename":"README.md","_rev":"1-033ff71704ceaf7b8f10a245b67e04c1"}