KafkaJS is still a widely used Kafka client for Node.js. npm counted about 3.6 million downloads in the week of 6–12 October 2026, and in August 2026 GitHub listed more than 32,000 public repositories depending on it. Its latest release, 2.2.4, shipped in February 2023. If you run KafkaJS today, or are about to inherit a service that does, here is what still works, what to watch, and how to stop the idle timer loop that newer Node.js versions now warn about.
TL;DR: In our test against a local Kafka 4.3.1 broker, KafkaJS 2.2.4 still produced, consumed, and ran Admin calls. It has had no release since February 2023 and does not support KIP-848 or GSSAPI. It also keeps a 1 ms timer running while a connection is idle, on every Node.js version we tested; Node.js 23 and later expose it through a warning. A small patch stops the timer.
For how KafkaJS compares with Platformatic Kafka, confluent-kafka-javascript, and node-rdkafka on Node.js support, consumer models, and Schema Registry, see Kafka JavaScript & Node.js Clients: KafkaJS vs Platformatic.
What still works
- No native addon. KafkaJS is pure JavaScript and declares support for Node.js 14+, so there is no librdkafka binary to build or ship for each platform.
-
A familiar Promise API. Producers, consumers with
eachMessageandeachBatchhandlers, transactions, headers, and an Admin client. - Common authentication. PLAIN, SCRAM, and OAUTHBEARER. For Amazon MSK IAM, AWS's aws-msk-iam-sasl-signer-js README shows KafkaJS using the signer as an OAUTHBEARER token provider.
- Kafka 4.x brokers. Against a local Kafka 4.3.1 broker, KafkaJS 2.2.4 created a topic, produced records, consumed them in a consumer group, and deleted the topic and group without errors.
What to watch
No releases since February 2023
The latest npm release is 2.2.4, published on 27 February 2023, and the last commit on the default branch was on 16 May 2024. As of 28 August 2026, the repository had 355 open issues and 62 open pull requests, and no pull request had been merged in the previous 90 days. Fixes proposed as pull requests, including the two below, are not being merged or released.
An idle 1 ms timer behind TimeoutNegativeWarning
On Node.js 23 and later, including the Node.js 24 LTS line, connecting a KafkaJS producer, consumer, or admin client prints a warning like this:
(node:95450) TimeoutNegativeWarning: -1791964353628 is a negative number.
Timeout duration was set to 1.
The warning is new, but the behavior behind it is not. After a connection receives its first response, KafkaJS's request queue schedules a check for pending requests. When the connection is not throttled, the delay it computes is negative (-1 - Date.now() by default), and Node.js runs a timer with a negative delay after 1 ms. Each check schedules the next one, so the loop keeps running while the connection is idle. The loop runs on Node.js 20 and 22 as well; of the versions we tested (20, 22, 23, 24, and 26), only 23 and later printed the warning. The report is issue #1751, open since May 2025.
The cost is small but constant. We ran the script below three times per configuration on an Apple Silicon Mac, with one idle, connected producer and a single-node Kafka 4.3.1 broker. It counts timers created with a delay below 1 ms and the process's CPU time over 10 seconds:
| Node.js | KafkaJS 2.2.4 | With the patch below |
|---|---|---|
| 22.20.0 | about 8,760 short timers, 253–303 ms CPU | 0 short timers, 43–44 ms CPU |
| 24.10.0 | about 8,760 short timers, 202–218 ms CPU | 0 short timers, 14–15 ms CPU |
That is roughly 2–3% of one core for a process that is doing nothing.
Reproduction script
// node repro.js localhost:9092 (kafkajs@2.2.4, a test broker running)
const { Kafka, logLevel } = require('kafkajs')
// Count timers created with a delay below 1 ms; Node.js runs them after 1 ms
let shortTimers = 0
const realSetTimeout = global.setTimeout
global.setTimeout = (fn, ms, ...args) => {
if (ms < 1) shortTimers++
return realSetTimeout(fn, ms, ...args)
}
const main = async () => {
const kafka = new Kafka({ brokers: [process.argv[2] || 'localhost:9092'], logLevel: logLevel.NOTHING })
const producer = kafka.producer()
await producer.connect()
// Measure 10 seconds of an idle, connected producer
shortTimers = 0
const start = process.cpuUsage()
await new Promise(resolve => realSetTimeout(resolve, 10_000))
const { user, system } = process.cpuUsage(start)
console.log(`${process.version}: ${shortTimers} short timers, ${Math.round((user + system) / 1000)} ms CPU in 10 s`)
await producer.disconnect()
}
main()
Suppressing the warning or staying on Node.js 22 hides the message but keeps the loop. The two open pull requests for this issue, #1755 and #1768, clamp the delay to 1 ms or 0 ms. In our test, both removed the warning, but the timer kept firing at the same rate.
A patch posted in the issue thread stops the loop instead: when no request is pending and the connection is not throttled, it skips scheduling the check. In node_modules/kafkajs/src/network/requestQueue/index.js, scheduleCheckPendingRequests() becomes:
scheduleCheckPendingRequests() {
let scheduleAt = this.throttledUntil - Date.now()
if (!this.throttleCheckTimeoutId) {
if (this.pending.length > 0) {
scheduleAt = scheduleAt > 0 ? scheduleAt : CHECK_PENDING_REQUESTS_INTERVAL
} else if (scheduleAt <= 0) {
// Nothing is pending and the connection is not throttled
return
}
this.throttleCheckTimeoutId = setTimeout(() => {
this.throttleCheckTimeoutId = null
this.checkPendingRequests()
}, scheduleAt)
}
}
To keep the change across installs with npm, use patch-package:
- Run
npm install patch-package. Keep it a regular dependency if production installs runnpm installornpm ci, because thepostinstallscript runs there too. - Add
"postinstall": "patch-package"to yourpackage.jsonscripts. If you already have apostinstallscript, append&& patch-packageto it. - Edit the file above, then run
npx patch-package kafkajs. This writespatches/kafkajs+2.2.4.patch. - Commit the
patchesdirectory.
pnpm and Yarn 2+ have built-in equivalents, pnpm patch and yarn patch.
Skipping the check is safe for queued requests because push() calls scheduleCheckPendingRequests() again whenever a request has to wait. With the patch, a producer and a consumer group still produced and consumed records against the same broker. It is still an unofficial change to a dependency, so test it under your own load, including broker throttling.
Classic consumer groups only
KafkaJS implements the classic consumer group protocol with eager rebalancing only. When the group rebalances, each member stops fetching from all of its partitions and rejoins, and consumption resumes only after the new assignment arrives, so every rebalance pauses the whole group (stop-the-world). Cooperative rebalancing (KIP-429), which lets members keep consuming partitions that are not moving, needs support in the client's rebalance handling, not only in the assigner. A custom assigner can change which member gets which partition, but it cannot remove the pause. KafkaJS also does not support KIP-848, the broker-driven protocol that became generally available in Kafka 4.0. Kafka 4.x brokers still support the classic protocol.
Authentication and compression gaps
- No GSSAPI/Kerberos. If your cluster requires Kerberos, KafkaJS cannot connect to it.
-
Only GZIP is built in. Snappy, LZ4, and ZSTD come from separate codec packages,
kafkajs-snappy,kafkajs-lz4, and@kafkajs/zstd, last published in 2018, 2020, and 2021. Register the codec in every process that produces or consumes those records. -
Schema Registry is a separate package.
@kafkajs/confluent-schema-registryis still released (4.1.0 in May 2026), and its 4.x line requires Node.js 22+.
If you need what KafkaJS lacks
A stable service that does not need these features can keep running KafkaJS. If you need KIP-848, Kerberos, or active maintenance, two clients publish KafkaJS migration guides:
-
confluent-kafka-javascript keeps
eachMessageandeachBatchthrough a KafkaJS-compatible API and is built on librdkafka, so it supports KIP-848. It adds a native addon to your deployment. For GSSAPI, the maintainers note that you need a librdkafka build with GSSAPI support, which also depends on system libraries, so verify it in your target environment. - Platformatic Kafka stays pure JavaScript and supports KIP-848. It requires Node.js 22.22+ or 24.6+, and consumers move to Node.js streams.
For what changes in each case, see the migration paths in the full comparison.
Check what your service actually does
Whether you patch KafkaJS or replace it, compare before and after. A Kafka desktop client such as Kafma shows each record's key, headers, partition, and raw bytes, and a consumer group's committed offsets and lag, which helps you spot changes in what your service writes and how its offsets move. Rebalances, retries, and crash recovery still need tests in your application.
Written by the team behind Kafma.
Top comments (0)