<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/postgres-kafka-in-node-js-without-the-dual-write-nightmare-pilm6bzki" -->

---
title: Postgres → Kafka in Node.js without the dual-write nightmare
description: The dual-write problem — where a database commit succeeds but a Kafka publish fails — is a common silent bug in event-driven Node.js services. The...
canonical: https://daily.dev/posts/postgres-kafka-in-node-js-without-the-dual-write-nightmare-pilm6bzki
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Postgres → Kafka in Node.js without the dual-write nightmare | daily.dev
og:description: The dual-write problem — where a database commit succeeds but a Kafka publish fails — is a common silent bug in event-driven Node.js services. The...
og:url: https://daily.dev/posts/postgres-kafka-in-node-js-without-the-dual-write-nightmare-pilm6bzki
og:image: https://api.daily.dev/og/posts/Pilm6bzKI.png
og:image:alt: Postgres → Kafka in Node.js without the dual-write nightmare
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Postgres → Kafka in Node.js without the dual-write nightmare

**[Samet GOKTEPE](https://daily.dev/sources/5nziq5ts7ilfmddl5h2oi)** · [@sametgoktepe](https://daily.dev/sametgoktepe) · 2 min read · 3 upvotes · 0 comments

## Summary

The dual-write problem — where a database commit succeeds but a Kafka publish fails — is a common silent bug in event-driven Node.js services. The transactional outbox pattern solves this by writing events into an outbox table within the same database transaction as business data, then using a background relay to ship them to the broker. eventferry is a new open-source Node.js/TypeScript library implementing this pattern with strict per-aggregate ordering across concurrent relays, crash-recovery reaper with visibility timeouts, retry/backoff with jitter and DLQ routing, LISTEN/NOTIFY waker plus optional WAL streaming, Confluent Schema Registry support (Avro/Protobuf/JSON Schema), W3C trace propagation, and a zero-dependency typed core. It positions itself as a lighter alternative to Debezium (which requires JVM + Kafka Connect) or job queues like pg-boss/BullMQ that don't provide atomic dual-write guarantees.

## Content

You write to your database. You publish an event to Kafka. The DB commit succeeds — the publish fails.

Now two systems silently disagree about what just happened. The DB thinks the order was placed; downstream services never heard about it. Quietly broken — until production breaks.

This is the dual-write problem, and it's the most common silent bug in event-driven Node.js services.

The fix has a name: the transactional outbox pattern. Write the event into an outbox table in the SAME transaction as your business data, and have a background relay reliably ship it to the broker.

A well-known pattern — but most Node.js implementations get the corners wrong: strict per-aggregate ordering under concurrent relays, crash-recovery reaper, retry/backoff math, DLQ routing.

So I built 🛳️ eventferry — a small open-source toolkit that solves it.

Why a new library:

• Debezium is great, but a JVM + Kafka Connect cluster to operate

• pg-boss / BullMQ are job queues, not outboxes (no atomic dual-write)

• DIY outbox works until you hit ordering + reaper + retries

What's inside:

✅ Strict per-aggregate ordering across N concurrent relays

🔄 Crash-recovery reaper (visibility timeout)

🔁 Retries with backoff + jitter, DLQ routing

⚡ LISTEN/NOTIFY waker + optional WAL streaming relay

📦 Confluent Schema Registry (Avro / Protobuf / JSON Schema)

🧭 W3C trace propagation (OpenTelemetry-ready)

🪶 Zero-dependency core, typed event registry

The whole pattern in one snippet:

await store.enqueue(client, {

topic: "orders.created",

aggregateType: "order",

aggregateId: [order.id](http://order.id),

payload: { orderId: [order.id](http://order.id), total: [order.total](http://order.total) },

}); // ← inside your business tx

await relay.start(); // background — ships to Kafka reliably

PostgreSQL ships today. MySQL, SQL Server, and MongoDB are the next adapters on the roadmap. MIT-licensed.

📦 npm: [https://www.npmjs.com/package/@eventferry/all](https://www.npmjs.com/package/@eventferry/all)

💻 GitHub: [https://github.com/SametGoktepe/eventferry](https://github.com/SametGoktepe/eventferry)

If you've tried Debezium / pg-boss / DIY for this — landed somewhere good or got bitten — I'd love to hear about it.

#nodejs #typescript #postgresql #kafka #microservices #eventdriven #opensource #backend

## Similar posts on daily.dev

- [How to Fix the Dual-Write Problem in Node.js with the Outbox Pattern](https://daily.dev/posts/how-to-fix-the-dual-write-problem-in-node-js-with-the-outbox-pattern-kobnx54pc) · freeCodeCamp · 1 upvotes · 0 comments

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#postgresql](https://daily.dev/tags/postgresql), [#kafka](https://daily.dev/tags/kafka)

[View this post on daily.dev](https://daily.dev/posts/postgres-kafka-in-node-js-without-the-dual-write-nightmare-pilm6bzki)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"DiscussionForumPosting","mainEntityOfPage":"https://daily.dev/posts/postgres-kafka-in-node-js-without-the-dual-write-nightmare-pilm6bzki","headline":"Postgres → Kafka in Node.js without the dual-write nightmare","text":"The dual-write problem — where a database commit succeeds but a Kafka publish fails — is a common silent bug in event-driven Node.js services. The transactional outbox pattern solves this by writing events into an outbox table within the same database transaction as business data, then using a background relay to ship them to the broker. eventferry is a new open-source Node.js/TypeScript library implementing this pattern with strict per-aggregate ordering across concurrent relays, crash-recovery reaper with visibility timeouts, retry/backoff with jitter and DLQ routing, LISTEN/NOTIFY waker plus optional WAL streaming, Confluent Schema Registry support (Avro/Protobuf/JSON Schema), W3C trace propagation, and a zero-dependency typed core. It positions itself as a lighter alternative to Debezium (which requires JVM + Kafka Connect) or job queues like pg-boss/BullMQ that don't provide atomic dual-write guarantees.","url":"https://daily.dev/posts/postgres-kafka-in-node-js-without-the-dual-write-nightmare-pilm6bzki","datePublished":"2026-06-16T20:45:55.342Z","dateModified":"2026-06-16T20:46:14.751Z","author":{"@type":"Person","name":"Samet GOKTEPE","url":"https://daily.dev/sametgoktepe","image":"https://media.daily.dev/image/upload/s--HAvMHvrX--/f_auto/v1781641597/avatars/avatar_5NziQ5ts7ILfMDDL5H2OI?_a=BAMAMiWQ0","description":"Hello there","interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"EndorseAction"},"userInteractionCount":450}},"image":"https://media.daily.dev/image/upload/s--6UayxrFl--/f_auto/v1781642755/posts/Pilm6bzKI?_a=BAMAMiWQ0","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"isPartOf":{"@type":"WebPage","url":"https://daily.dev/sources/5nziq5ts7ilfmddl5h2oi","name":"Samet GOKTEPE"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Samet GOKTEPE","item":"https://daily.dev/sources/5nziq5ts7ilfmddl5h2oi"},{"@type":"ListItem","position":3,"name":"Postgres → Kafka in Node.js without the dual-write nightmare"}]}
```

