🏰 Taming the Data Chaos: Your First Adventure with Kafka & Node.js
Imagine you’ve just launched a new online store. Orders are trickling in, and everything is running smoothly. Your “Order Service” gets an…
🏰 Taming the Data Chaos: Your First Adventure with Kafka & Node.js

Imagine you’ve just launched a new online store. Orders are trickling in, and everything is running smoothly. Your “Order Service” gets an order and tells the “Processing Service” what to do. Simple!
But then, a flash sale goes viral. 🚀 Suddenly, thousands of orders flood in every minute. Your Processing Service, like a frantic chef with too many tickets, gets overwhelmed and crashes. The Order Service tries to send more orders, but there’s nowhere for them to go. New orders fail, customers are angry, and your dream launch becomes a nightmare.
This is the chaos of a tightly-coupled system. When one part fails or slows down, the whole system grinds to a halt.
What if you had a magical, infinitely long conveyor belt between your services? The Order Service could place orders on the belt as fast as it wants, and the Processing Service could pick them off at its own pace. Even if the processor stops for a while, the orders just queue up safely on the belt, waiting to be handled.
That magical conveyor belt is Apache Kafka. It’s not just a message queue; it’s a distributed streaming platform that acts as the central nervous system for modern applications. Today, we’ll build that exact system.
🤔 “But Wait, What About WebSockets or SSE?”
That’s a great question! Tools like WebSockets and Server-Sent Events (SSE) are fantastic for real-time communication between a user’s browser and a server — think of a live chat app or a stock ticker. It’s like a telephone call; the connection is direct and immediate.
Kafka solves a different problem. It’s for communication between your back-end services. It’s less like a phone call and more like a highly reliable, organized post office.
- A service (Producer) drops off a letter (a message or event) at the post office.
- It doesn’t care who picks it up or when. Its job is done.
- Other services (Consumers) can subscribe to specific mailboxes (Topics) and pick up letters whenever they’re ready.
This “post office” model is what prevents the chaos we described earlier.
💥 The Problem We’re Solving: The Domino Effect
Without Kafka, your system is a line of dominoes.
- What if the consumer is down? 😨 The producer can’t send data. The order is lost. Domino falls.
- What if the consumer is slow? 🐌 The producer has to wait, slowing down the entire system. Domino wobbles.
- What if you get a huge spike in orders? 🌊 The consumer gets overwhelmed and crashes. All dominoes fall.
- What if you want to add another service (like an email notification service)? 🤔 You have to change the producer’s code to tell it about this new service, making your system more brittle.
✨ The Kafka Solution: A Resilient, Decoupled System
By placing Kafka in the middle, you break these direct dependencies.
- Resilience 🛡️: If the consumer crashes, the producer keeps sending orders to Kafka. The messages wait safely in the
orderstopic until the consumer is back online. No data is lost. - Speed 💨: The producer sends a message and immediately moves on. It doesn’t wait for the consumer, making your application incredibly fast and responsive.
- Buffering 🌊: That sudden spike of 10,000 orders? Kafka holds them all, acting as a buffer. The consumer can process them at its own pace without being overwhelmed.
- Scalability ✨: Is one consumer too slow? Just launch five more! Kafka automatically distributes the work among them, processing orders five times faster without any code changes in the producer.
Now, let’s stop talking and start building!
🛠️ The Blueprint: Our E-Commerce Order System
We’ll build a simple system with two main parts:
- Producer: A Node.js script that creates fake e-commerce order data every second and sends it to a Kafka topic called
orders. - Consumer: Another Node.js script that listens to the
orderstopic, receives the data, and prints it to the console.
Folder Structure
First, create a main folder called kafka-order-system. Inside, your structure will look like this:
kafka-order-system/
├── src/
│ ├── producer.js
│ └── consumer.js
├── docker-compose.yml
├── package.json
└── package-lock.json
🚀 Let’s Build! A Step-by-Step Guide
Step 1: 🐳 Laying the Foundation with Docker
We need Kafka to run, but installing it manually can be tricky. Instead, we’ll use Docker to spin up Kafka, its dependency Zookeeper, and a handy UI called Kafdrop with one command.
Create the docker-compose.yml file in your project's root and add this:
version: '3.8'
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.3.2
hostname: zookeeper
container_name: zookeeper
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:7.3.2
hostname: kafka
container_name: kafka
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
kafdrop:
image: obsidiandynamics/kafdrop:latest
container_name: kafdrop
depends_on:
- kafka
ports:
- "9000:9000"
environment:
KAFKA_BROKERCONNECT: "kafka:29092"
Step 2: 📦 Setting Up Our Node.js Workshop
Now, let’s get our Node.js environment ready. Open your terminal in the kafka-order-system directory.
- Initialize npm:
npm init -y- Install Dependencies: We need
kafkajsto talk to Kafka and@faker-js/fakerto generate our dummy data. npm install kafkajs @faker-js/faker- Use ES Modules: To use the modern
importsyntax, add this line to yourpackage.jsonfile: "type": "module",
Step 3: ✍️ Writing the Application Code
Create the src folder and the two JavaScript files inside it.
The Producer (src/producer.js)
This script is our “Order Service.” It will create a fake order every second and drop it into the orders topic in Kafka.
import { Kafka } from 'kafkajs';
import { faker } from '@faker-js/faker';
// 1. Create a Kafka client
const kafka = new Kafka({
clientId: 'my-producer',
brokers: ['localhost:9092'], // Kafka broker address
});
const producer = kafka.producer();
const topic = 'orders';
const runProducer = async () => {
try {
// 2. Connect the producer
await producer.connect();
console.log("✅ Producer connected successfully.");
// 3. Send a new message every second
setInterval(async () => {
const orderMessage = {
orderId: faker.string.uuid(),
product: faker.commerce.productName(),
quantity: faker.number.int({ min: 1, max: 5 }),
price: parseFloat(faker.commerce.price()),
customer: {
name: faker.person.fullName(),
email: faker.internet.email(),
},
timestamp: new Date().toISOString(),
};
await producer.send({
topic: topic,
messages: [{ value: JSON.stringify(orderMessage) }],
});
console.log(`📦 Sent order: ${orderMessage.orderId}`);
}, 1000); // Send a message every 1000ms (1 second)
} catch (error) {
console.error("❌ Error connecting or sending message:", error);
}
};
runProducer();
The Consumer (src/consumer.js)
This script is our “Processing Service.” It connects to Kafka, subscribes to the orders topic, and logs every message it receives.
import { Kafka } from 'kafkajs';
// 1. Create a Kafka client
const kafka = new Kafka({
clientId: 'my-consumer',
brokers: ['localhost:9092'],
});
const consumer = kafka.consumer({ groupId: 'order-processing-group' });
const topic = 'orders';
const runConsumer = async () => {
try {
// 2. Connect the consumer
await consumer.connect();
console.log("✅ Consumer connected successfully.");
// 3. Subscribe to the topic
await consumer.subscribe({ topic: topic, fromBeginning: true });
// 4. Run the consumer to listen for messages
await consumer.run({
eachMessage: async ({ topic, partition, message }) => {
const order = JSON.parse(message.value.toString());
console.log(`📥 Received new order!
Topic: ${topic}
Partition: ${partition}
Order ID: ${order.orderId}
Product: ${order.product}
`);
},
});
} catch (error) {
console.error("❌ Error connecting or running consumer:", error);
}
};
runConsumer();
Step 4: 🎉 The Grand Finale: Run Everything!
It’s time to see the magic happen. You’ll need two separate terminal windows.
- Start Kafka: In your first terminal, at the root of your project, start the Docker containers.
docker-compose up -d- This might take a minute the first time.
- Start the Consumer: In the same terminal, start the consumer. It will connect and wait patiently for orders.
node src/consumer.js- You should see:
✅ Consumer connected successfully. - Start the Producer: Now, open a second terminal window. Start the producer to begin generating data.
node src/producer.js- You should see:
✅ Producer connected successfully.followed by📦 Sent order: ...every second.
Expected Outcome: Your consumer terminal will immediately spring to life, printing 📥 Received new order! logs as messages arrive from the producer, relayed perfectly through Kafka.
See it for Yourself with Kafdrop UI
Open your browser and go to http://localhost:9000. This is the Kafdrop UI. You can click on the orders topic to see the messages flowing in real-time. It's a fantastic way to confirm that your data is moving through the system visually.
To Stop Everything
- Stop the producer and consumer scripts in their terminals by pressing
Ctrl + C. - Stop the Kafka environment with this command:
docker-compose down
💡 Enhanced Tips for Your Kafka Journey
Congratulations! You’ve just built your first event-driven system. Here are a few tips as you explore further:
- Consumer Groups are Key: The
groupId: 'order-processing-group'In our consumer, it is powerful. If you start another consumer with the same group ID, Kafka will automatically balance the orders between them, giving you instant scalability. - Think About Data Schema: In a real-world app, you’d want to enforce a structure for your messages. Tools like Avro help ensure that producers and consumers always agree on the data format.
- Handle Errors Gracefully: What if your consumer fails to process a message? Kafka has built-in retry mechanisms and concepts like “Dead Letter Queues” to handle failures without losing data.
- Explore Partitions: A Kafka topic is divided into partitions. This is how it achieves massive parallelism. Messages with the same key (e.g., a customer ID) can be sent to the same partition to guarantee order of processing for that specific customer.
🙏 Thank You!
Thank you for following along on this adventure! You’ve taken a massive step from traditional, brittle software design to building a modern, resilient, and scalable system. The principles you’ve learned here are the bedrock of the applications that power our digital world.
Happy coding!
메타데이터
- post_id
- b51884820e33
- slug
- taming-the-data-chaos-your-first-adventure-with-kafka-node-js-b51884820e33
- url
- https://medium.com/@topi9864/taming-the-data-chaos-your-first-adventure-with-kafka-node-js-b51884820e33
- canonical_url
- https://medium.com/@topi9864/taming-the-data-chaos-your-first-adventure-with-kafka-node-js-b51884820e33
- author_url
- https://medium.com/@topi9864
- status
- ok
- fetched_at
- 2026-07-17 01:49:50