<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kimsang Mok</title>
    <description>The latest articles on DEV Community by Kimsang Mok (@kimsang_mok).</description>
    <link>https://dev.to/kimsang_mok</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3124113%2Fdf23c897-fa3c-48a5-b4d0-da8ce0351fa2.png</url>
      <title>DEV Community: Kimsang Mok</title>
      <link>https://dev.to/kimsang_mok</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kimsang_mok"/>
    <language>en</language>
    <item>
      <title>Messaging Made Simple with RabbitMQ-Stream</title>
      <dc:creator>Kimsang Mok</dc:creator>
      <pubDate>Mon, 05 May 2025 02:32:16 +0000</pubDate>
      <link>https://dev.to/kimsang_mok/messaging-made-simple-with-rabbitmq-stream-4c82</link>
      <guid>https://dev.to/kimsang_mok/messaging-made-simple-with-rabbitmq-stream-4c82</guid>
      <description>&lt;p&gt;&lt;em&gt;A lightweight RabbitMQ framework for Node.js, built to eliminate boilerplate and let you focus on business logic.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Messaging and Event-Driven Architecture?
&lt;/h2&gt;

&lt;p&gt;Let’s say you’re building an app with several moving parts—maybe one service processes user signups, another sends welcome emails, and another tracks analytics. Normally, these services would need to talk to each other directly and immediately—which makes things tightly connected and harder to scale.&lt;/p&gt;

&lt;p&gt;Messaging and event-driven architecture solve this by letting each part send and receive information independently. It works like this:&lt;/p&gt;

&lt;p&gt;When something important happens (like a new user signing up), that part of the app sends a message—think of it like dropping a letter in a mailbox.&lt;br&gt;
Other parts of the app can listen for those messages and react when they arrive.&lt;br&gt;
The middleman that passes the message along is called a message broker—and one of the most popular is &lt;a href="https://www.rabbitmq.com/" rel="noopener noreferrer"&gt;RabbitMQ&lt;/a&gt;.&lt;br&gt;
This setup means the sender and the receiver don’t have to be active at the same time. One service can keep moving, and the rest will catch up.&lt;/p&gt;

&lt;p&gt;Here’s a simple example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A user signs up → The app sends a user.created message → The &amp;gt; email service sends a welcome email, the analytics service&lt;br&gt;
logs it, and the CRM updates the user record.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With messaging, your system becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale better&lt;/strong&gt; under load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay responsive&lt;/strong&gt; to users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry failures&lt;/strong&gt; without crashing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add new features&lt;/strong&gt; without tightly coupling components&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;As the RabbitMQ docs explain:&lt;/p&gt;

&lt;p&gt;“A message broker decouples producers and consumers, letting your applications communicate reliably and asynchronously.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The Pain of Using RabbitMQ in Node.js
&lt;/h2&gt;

&lt;p&gt;RabbitMQ is a powerful message broker, but working with it directly in Node.js—especially with low-level libraries like &lt;a href="https://www.npmjs.com/package/amqplib" rel="noopener noreferrer"&gt;&lt;code&gt;amqplib&lt;/code&gt;&lt;/a&gt;—often means &lt;em&gt;tons of boilerplate&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here’s what that typically looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Traditional setup with amqplib&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;amqp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqplib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;amqp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqp://localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createChannel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.created.queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.exchange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;topic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;durable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;durable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// business logic&lt;/span&gt;
        &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// manual retry or DLQ&lt;/span&gt;
        &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now multiply that setup across every service in your system.&lt;/p&gt;

&lt;p&gt;Then add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retry logic with exponential backoff&lt;/li&gt;
&lt;li&gt;Delayed messages&lt;/li&gt;
&lt;li&gt;Dead-letter queues (DLQs)&lt;/li&gt;
&lt;li&gt;Reconnection handling&lt;/li&gt;
&lt;li&gt;Logging and tracing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It gets overwhelming quickly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As &lt;a href="https://medium.com/safe-engineering/handling-message-retries-in-rabbitmq-with-delay-7de3a93d9afd" rel="noopener noreferrer"&gt;this article&lt;/a&gt; points out:&lt;/p&gt;

&lt;p&gt;“Tracking and managing failures can become a daunting task without a proper retry mechanism.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without structured retries, many teams push failed messages directly to DLQs, missing the chance to recover from temporary failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing: &lt;code&gt;rabbitmq-stream&lt;/code&gt; ✨
&lt;/h2&gt;

&lt;p&gt;That’s why I built &lt;a href="https://www.npmjs.com/package/rabbitmq-stream" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;code&gt;rabbitmq-stream&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; – a lightweight, decorator-driven framework that removes all that boilerplate from your Node.js app.&lt;/p&gt;

&lt;p&gt;Inspired by &lt;a href="https://spring.io/projects/spring-cloud-stream" rel="noopener noreferrer"&gt;Spring Cloud Stream&lt;/a&gt;, it brings a &lt;strong&gt;declarative&lt;/strong&gt; and &lt;strong&gt;pluggable&lt;/strong&gt; model to RabbitMQ.&lt;/p&gt;

&lt;p&gt;Instead of writing 50+ lines of setup, you just configure messaging once and focus on the code that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;rabbitmq-stream&lt;/code&gt;, you start by creating a &lt;em&gt;Messaging Context&lt;/em&gt;—usually in your app entry point—where you define the RabbitMQ connection and all input/output bindings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// messaging.config.ts&lt;/span&gt;
&lt;span class="nf"&gt;createMessagingContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqp://localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;binder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;userCreated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.created.queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.exchange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;routingKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;userCreated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.exchange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;routingKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use decorators in your services to write publishers and consumers like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;MessagingService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userCreated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handleUserCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserCreatedEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New user created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userCreated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserCreatedEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removes manual setup of channels, queues, bindings&lt;/li&gt;
&lt;li&gt;Automatically handles retries, delays, dead-lettering&lt;/li&gt;
&lt;li&gt;Lets you focus 100% on writing business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative Messaging Config&lt;/strong&gt; via &lt;code&gt;createMessagingContext&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decorators&lt;/strong&gt; for consumers and publishers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic retries&lt;/strong&gt; with TTL queues and exponential backoff&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delayed message&lt;/strong&gt; support (via TTL or plugin)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead-letter queues&lt;/strong&gt; for messages that fail repeatedly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection strategy&lt;/strong&gt; with jitter, fixed, or exponential backoff&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal boilerplate&lt;/strong&gt;, clean DI-friendly services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Focus on Business Logic, Not Plumbing
&lt;/h2&gt;

&lt;p&gt;The best part? This project was born from &lt;strong&gt;real-world frustration&lt;/strong&gt;. I (the author) got tired of writing the same RabbitMQ plumbing in every Node.js service. So many hours were spent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repeating the same AMQP boilerplate&lt;/li&gt;
&lt;li&gt;Writing retry/delay logic manually&lt;/li&gt;
&lt;li&gt;Debugging reconnect failures&lt;/li&gt;
&lt;li&gt;Spending too much time wiring things that should be automatic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Here’s my queue and routing key. Here’s the function that should handle it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rabbitmq-stream&lt;/code&gt; is my attempt to bring the productivity of Spring Cloud Stream to the Node.js world.&lt;/p&gt;

&lt;p&gt;It lets you stay in the zone with your business logic. Define your messaging topology once, then write plain methods with decorators. For example, you might simply do &lt;code&gt;@Publisher("orderCreated")&lt;/code&gt; on a method that returns an &lt;code&gt;OrderEvent—&lt;/code&gt;and rabbitmq-stream will send that event to the &lt;code&gt;order.created&lt;/code&gt; exchange with minimal fuss. Under the hood it’ll acknowledge messages, retry if they fail, or forward them to a dead-letter queue if needed—all configurable. The idea is to treat messaging in Node just like any other function call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Getting started is easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="nx"&gt;rabbitmq&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define your messaging context&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;@MessagingService&lt;/code&gt;, &lt;code&gt;@Publisher&lt;/code&gt;, and &lt;code&gt;@Consumer&lt;/code&gt; to your classes and methods&lt;/li&gt;
&lt;li&gt;Enjoy clean, testable, resilient messaging logic&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;✨ Whether you’re building APIs, microservices, or event-driven workflows, &lt;code&gt;rabbitmq-stream&lt;/code&gt; aims to make RabbitMQ feel developer-friendly. Instead of wrestling with low-level AMQP code, you get a Spring-Cloud-Stream-inspired model in Node.js. So write your publishers and consumers as simple async methods—let the framework handle the rest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Open Source and Built for Collaboration
&lt;/h2&gt;

&lt;p&gt;This project is open-source and MIT licensed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/kimsang-mok/rabbitmq-stream" rel="noopener noreferrer"&gt;github.com/kimsang-mok/rabbitmq-stream&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NPM: &lt;a href="https://www.npmjs.com/package/rabbitmq-stream" rel="noopener noreferrer"&gt;npmjs.com/package/rabbitmq-stream&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find it helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star it&lt;/li&gt;
&lt;li&gt;Open issues or bugs&lt;/li&gt;
&lt;li&gt;Suggest improvements&lt;/li&gt;
&lt;li&gt;Contribute&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;RabbitMQ JavaScript Tutorials: &lt;a href="https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html" rel="noopener noreferrer"&gt;https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A tale of retries using RabbitMQ (Medium): &lt;a href="https://medium.com/@mjmachado/implementing-retry-strategy-on-rabbitmq-messages-with-ttl-and-dead-letter-queue-727ad2b18048" rel="noopener noreferrer"&gt;https://medium.com/@mjmachado/implementing-retry-strategy-on-rabbitmq-messages-with-ttl-and-dead-letter-queue-727ad2b18048&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Spring Cloud Stream: &lt;a href="https://spring.io/projects/spring-cloud-stream" rel="noopener noreferrer"&gt;https://spring.io/projects/spring-cloud-stream&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Let me know what you think. Let’s make messaging easier for everyone.&lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>node</category>
      <category>microservices</category>
      <category>npm</category>
    </item>
  </channel>
</rss>
