<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Learn Kafka]]></title><description><![CDATA[Learn Kafka]]></description><link>https://sumit70.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 28 Oct 2026 13:26:49 GMT</lastBuildDate><atom:link href="https://sumit70.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Apache Kafka for Web Developers: Your First Producer and Consumer in Node.js]]></title><description><![CDATA[Learn Apache Kafka by building a producer and consumer in Node.js. Topics, partitions, consumer groups and replay, explained with real, tested output.]]></description><link>https://sumit70.hashnode.dev/apache-kafka-for-web-developers-your-first-producer-and-consumer-in-node-js</link><guid isPermaLink="true">https://sumit70.hashnode.dev/apache-kafka-for-web-developers-your-first-producer-and-consumer-in-node-js</guid><category><![CDATA[kafka]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[webdev]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Sumit Vishwkarma]]></dc:creator><pubDate>Mon, 19 Oct 2026 13:17:22 GMT</pubDate><content:encoded><![CDATA[<p>I've built a lot of web apps where one action sets off five others. A customer places an order, and suddenly the app has to send an email, update analytics, notify the warehouse and maybe award loyalty points. The first version always looks the same: the checkout API calls every one of those services, one after another.</p>
<p>It works until it doesn't. The email provider is slow, so checkout is slow. Analytics is down, so checkout fails. And every new feature means editing the checkout code again.</p>
<p>That problem is exactly what Apache Kafka is built for. This is the first post in my Learn Kafka series. By the end of it you'll understand the core ideas and have a working producer and consumer running on your machine. Every command and every line of output below comes from a real run.</p>
<p>What Kafka actually is
Kafka is a distributed event log. Strip away the jargon and it's three simple ideas:</p>
<p>Something happened, so you write it down as an event: "order ord_101 was placed".
Kafka appends that event to a log and keeps it, for days or forever, depending on how you configure it.
Any number of services can read that log, each at its own pace, without knowing about each other.
The checkout service no longer calls five services. It just announces "an order was placed" and moves on. Whoever cares about orders listens.</p>
<p>A useful way to think about it: a message queue is like a to-do list where finished items get crossed out. Kafka is more like a diary. Entries stay, and anyone can read it from any page.</p>
<p>The six words you need
Term	What it means
Event (message)	One record, with a key, a value and a timestamp. Example: key u_1, value {"orderId":"ord_101"}
Topic	A named stream of events, like orders or payments. Think of it as a table name for events
Partition	A topic is split into partitions so it can scale. Each partition is an ordered, append-only log
Offset	The position of an event inside a partition: 0, 1, 2… Consumers remember the offset they've reached
Producer	Code that writes events to a topic
Consumer group	One or more consumers that share the work of reading a topic. Each group gets every event
The last one is where Kafka gets clever, and we'll see it in action in a minute.</p>
<p>Step 1: Run Kafka locally
Since Kafka 4.0, Kafka no longer needs ZooKeeper. It manages itself in what's called KRaft mode, so a local setup is much simpler than older tutorials suggest.</p>
<p>With Docker (easiest):</p>
<p>docker run -d --name kafka -p 9092:9092 apache/kafka:4.3.1
Without Docker: you need Java 17 or newer. Download Kafka from kafka.apache.org/downloads, extract it, then from inside the folder:</p>
<p>KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
bin/kafka-storage.sh format --standalone -t $KAFKA_CLUSTER_ID -c config/server.properties
bin/kafka-server-start.sh config/server.properties
Either way, you now have a broker listening on localhost:9092.</p>
<p>Step 2: Create a topic
We'll create an orders topic with 3 partitions:</p>
<h1>Docker</h1>
<p>docker exec -it kafka /opt/kafka/bin/kafka-topics.sh <br />  --create --topic orders --partitions 3 --bootstrap-server localhost:9092</p>
<h1>Without Docker</h1>
<p>bin/kafka-topics.sh --create --topic orders --partitions 3 --bootstrap-server localhost:9092
Created topic orders.
Step 3: Set up the Node.js project
For Node.js, I'm using Confluent's official client, @confluentinc/kafka-javascript. You'll see a lot of older tutorials use kafkajs, which hasn't been actively maintained for a while. The Confluent client includes a KafkaJS-style API, so the code will look familiar either way.</p>
<p>mkdir kafka-demo &amp;&amp; cd kafka-demo
npm init -y
npm pkg set type=module
npm install @confluentinc/kafka-javascript
Step 4: Write a producer
The producer plays the role of our checkout service. It sends three orders to the orders topic.</p>
<p>// producer.js
import { KafkaJS } from "@confluentinc/kafka-javascript";</p>
<p>const kafka = new KafkaJS.Kafka({
  kafkaJS: {
    clientId: "checkout-service",
    brokers: ["localhost:9092"],
    logLevel: KafkaJS.logLevel.ERROR,
  },
});</p>
<p>const producer = kafka.producer();
await producer.connect();</p>
<p>const orders = [
  { orderId: "ord_101", userId: "u_1", total: 499 },
  { orderId: "ord_102", userId: "u_2", total: 1299 },
  { orderId: "ord_103", userId: "u_1", total: 249 },
];</p>
<p>for (const order of orders) {
  await producer.send({
    topic: "orders",
    messages: [{ key: order.userId, value: JSON.stringify(order) }],
  });
  console.log(<code>Sent ${order.orderId}</code>);
}</p>
<p>await producer.disconnect();
Notice the key: we use userId. That matters, and we'll come back to it.</p>
<p>Step 5: Write a consumer
The consumer reads orders and prints them. We pass the consumer group name on the command line so we can run it as different services.</p>
<p>// consumer.js
import { KafkaJS } from "@confluentinc/kafka-javascript";</p>
<p>const groupId = process.argv[2] ?? "email-service";</p>
<p>const kafka = new KafkaJS.Kafka({
  kafkaJS: {
    clientId: groupId,
    brokers: ["localhost:9092"],
    logLevel: KafkaJS.logLevel.ERROR,
  },
});</p>
<p>const consumer = kafka.consumer({
  kafkaJS: { groupId, fromBeginning: true },
});</p>
<p>await consumer.connect();
await consumer.subscribe({ topics: ["orders"] });</p>
<p>await consumer.run({
  eachMessage: async ({ partition, message }) =&gt; {
    const order = JSON.parse(message.value.toString());
    console.log(
      <code>[${groupId}] partition ${partition}, offset ${message.offset}: </code> +
        <code>order ${order.orderId} for user ${order.userId}, total ₹${order.total}</code>
    );
  },
});
fromBeginning: true means that when a brand-new group starts, it reads the topic from the first event instead of only new ones.</p>
<p>Step 6: Watch consumer groups work
Open three terminals and start three consumers. Two belong to email-service, and one is analytics-service:</p>
<p>node consumer.js email-service      # terminal 1
node consumer.js email-service      # terminal 2
node consumer.js analytics-service  # terminal 3
Give them a few seconds to join, then in a fourth terminal run the producer:</p>
<p>node producer.js
Sent ord_101
Sent ord_102
Sent ord_103
Here's what each consumer printed on my machine:</p>
<h1>terminal 1 (email-service)</h1>
<p>[email-service] partition 2, offset 0: order ord_101 for user u_1, total ₹499
[email-service] partition 2, offset 1: order ord_103 for user u_1, total ₹249</p>
<h1>terminal 2 (email-service)</h1>
<p>[email-service] partition 1, offset 0: order ord_102 for user u_2, total ₹1299</p>
<h1>terminal 3 (analytics-service)</h1>
<p>[analytics-service] partition 2, offset 0: order ord_101 for user u_1, total ₹499
[analytics-service] partition 2, offset 1: order ord_103 for user u_1, total ₹249
[analytics-service] partition 1, offset 0: order ord_102 for user u_2, total ₹1299
Your partition numbers may differ, but the pattern will be the same. This small output shows the three most important ideas in Kafka.</p>
<ol>
<li><p>Different groups each get everything
analytics-service received all three orders, and so did email-service as a whole. Each group is an independent reader of the log. Adding a new service, say loyalty-service, means starting a consumer with a new group name. The checkout code doesn't change at all.</p>
</li>
<li><p>Consumers in the same group share the work
The two email-service consumers split the orders between them. Kafka gives each consumer in a group its own partitions, so no order gets emailed twice. If email gets busy, run more consumers in that group, up to one per partition. That's why the partition count matters: it's the ceiling on how many consumers in one group can work in parallel.</p>
</li>
<li><p>Same key, same partition, guaranteed order
Both orders from user u_1 landed in partition 2, in the order they were sent. Kafka sends every message with the same key to the same partition, and order is only guaranteed within a partition. So if the order of events per user, per account or per device matters to you, use that ID as the key.</p>
</li>
</ol>
<p>Step 7: Replay, Kafka's superpower
Stop a consumer and start it again with the same group name. It doesn't print the old orders again, because the group remembers its offset: how far it has read.</p>
<p>Now start one with a new group name:</p>
<p>node consumer.js reporting-service
It reads all three orders from the beginning, because the events are still in the log. That's something a traditional queue can't do. You can add a new service months later, or rebuild a broken report, by replaying history.</p>
<p>When you don't need Kafka
Kafka is powerful, but it's also one more system to run. You probably don't need it if:</p>
<p>you have a single app and a background job queue would do (BullMQ, a Postgres-backed queue, or your platform's queues);
you're handling a few events per minute and no other service needs them;
nobody on the team wants to run or pay for a cluster yet.
Kafka starts to pay off when several independent services react to the same events, when you need high throughput, or when being able to replay events actually matters to you.</p>
<p>Recap
Kafka is an event log: producers append, consumers read, events stay.
Topics are split into partitions. Each consumer tracks an offset.
Different groups each get every event. Consumers in one group share the partitions.
Keys decide the partition, and order is guaranteed within a partition.
Since Kafka 4.0 there's no ZooKeeper, so you can be up and running locally in one command.
Next in the series
In the next post, we'll make this production-shaped: what happens when a consumer crashes halfway through a message, how offset commits work, and how to avoid processing an order twice.</p>
<p>If this helped, follow the Learn Kafka series so you don't miss it, and tell me in the comments what you're planning to build with Kafka.</p>
]]></content:encoded></item></channel></rss>