DEV Community

KafmaKarma for Kafma

Posted on

KafkaJS in 2026: What Still Works and What to Watch

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 eachMessage and eachBatch handlers, 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.
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

To keep the change across installs with npm, use patch-package:

  1. Run npm install patch-package. Keep it a regular dependency if production installs run npm install or npm ci, because the postinstall script runs there too.
  2. Add "postinstall": "patch-package" to your package.json scripts. If you already have a postinstall script, append && patch-package to it.
  3. Edit the file above, then run npx patch-package kafkajs. This writes patches/kafkajs+2.2.4.patch.
  4. Commit the patches directory.

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-registry is 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 eachMessage and eachBatch through 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)