NATS Node Transport

The official Node.js and Bun TCP transport that connects the NATS.js client to a NATS server.

SDK
npm
v3.4.0
439stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
60/100Good
Development Activity32
Maintenance48
Community76
Maturity44
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
82/100Excellent
Architecture82
Code Quality80
Innovation74
Learning Curve90

@nats-io/transport-node is the official Node.js and Bun transport for the NATS.js client, implementing a native TCP socket connection to a NATS server. It re-exports the full NATS Core API — publish/subscribe, request/reply, and JetStream connectivity — so applications get a single connect() function that returns a fully-functional NatsConnection without wiring up a transport themselves.

As of the NATS.js v3 reorganization, the JavaScript clients were split into focused modules under the @nats-io scope: a runtime-agnostic core, and separate transports for Deno, Node/Bun, and the browser. This package is the Node/Bun transport, handling TCP dialing, TLS upgrade (including handshake-first TLS), DNS resolution, and socket lifecycle management, while delegating protocol parsing and connection semantics to @nats-io/nats-core.

What You Get

  • A single connect() function returning a ready-to-use NatsConnection backed by a native Node.js TCP socket
  • Automatic TLS negotiation, including server-required TLS, opportunistic TLS upgrade, and TLS-handshake-first mode for secured deployments
  • Full re-export of the @nats-io/nats-core API — publish/subscribe, request/reply, headers, and subscriptions — with no additional imports needed
  • DNS resolution and reconnection handling tuned for long-lived server connections in Node.js and Bun runtimes
  • Compatibility with the wider NATS.js module family (JetStream, KV, Object Store, Services) once installed alongside this transport

Common Use Cases

  • Connecting a Node.js or Bun backend service to a NATS cluster for pub/sub messaging
  • Building request/reply microservices on top of NATS Core without managing sockets directly
  • Powering JetStream-backed persistence, KV, or Object Store features in a Node.js app
  • Running TLS-secured connections to NATS deployments that require client certificates or handshake-first TLS

Under The Hood

Architecture The transport-node module sits at the edge of the NATS.js core/transport split introduced in the v3 monorepo reorg: connect() in connect.ts wires a NodeTransport (implementing the shared Transport interface from nats-base-client) into NatsConnectionImpl.connect(), so the socket lifecycle in node_transport.ts (dial/tlsFirst/startTLS/peekInfo/setupHandlers/_closed) is the only Node-specific code — everything else (protocol parsing, subscription management, JetStream, KV) is delegated to @nats-io/nats-core. This is a strict layered architecture: a transport is anything satisfying Transport, registered via setTransportFactory, so swapping this package for transport-deno changes nothing about the rest of the connection stack. Data flows one way at a time — inbound bytes arrive through the socket’s data event, are buffered and surfaced to the core parser via an AsyncIterableIterator, while outbound frames go straight through socket.write() — and every teardown path funnels through a single _closed() method regardless of whether it originated from the caller, a socket close event, or an error, avoiding the double-close races common in hand-rolled socket wrappers. If the shared Transport interface changed shape, every sibling transport package would need a matching update, but this module itself is easy to swap for any TCP-capable runtime.

Tech Stack Written in TypeScript (esnext target, nodenext module resolution) compiled to a lib/ output via tsc, with allowJs enabled for the existing JavaScript test files. Production dependencies are limited to @nats-io/nats-core (the protocol/runtime-agnostic client), @nats-io/nkeys, and @nats-io/nuid, with dev-only dependencies on @nats-io/jetstream, @nats-io/kv, @nats-io/nst (a NATS-server test harness), @nats-io/obj, typescript, shx, and minimist. There is no web framework or ORM here — this is a low-level networking package built directly on Node’s node:net, node:tls, node:dns, and node:fs built-ins, deliberately avoiding third-party TCP/TLS wrappers. Distribution is via npm under the @nats-io scope, versioned in lockstep with the other v3 modules and gated by a check-package script that enforces cross-module version consistency before publish.

Code Quality Tests use Node’s built-in node:test runner (describe/it) across five suites — basics, reconnect, tls, noiptls, and jetstream — spinning up real ephemeral NATS servers via @nats-io/nst’s NatsServer.start() rather than mocking the socket layer, which gives strong confidence that TLS upgrade paths and reconnect logic actually work end-to-end rather than just against stubs. Error handling is explicit and typed: connection failures are normalized into errors.ConnectionError/errors.InvalidArgumentError from the shared core error module rather than left as raw Node socket errors, and the one place a raw error could leak is defensively checked with a comment explaining the underlying Kubernetes/istio-init edge case that prompted it. TypeScript is used throughout with declaration output enabled, though a handful of @ts-ignore escapes exist around Node’s untyped socket internals. CI runs a dedicated transport-node-test workflow plus repo-wide node, deno, consistency, and “unsafe” checks workflows, giving multi-angle verification beyond a single test job.

API Design The public surface is deliberately tiny — a single connect(opts) call is the only export an application typically needs, and it returns the same NatsConnection object used by every other NATS.js transport, so switching from Deno to Node requires changing one import, not any calling code. Options extend the shared ConnectionOptions type with only a Node-specific tls shape, keeping the cross-runtime API surface consistent with this repo’s own stated goal of keeping the core client’s API in parity across every official NATS client language. Errors are rejected as instances of a small typed errors hierarchy instead of generic Error objects or strings, so callers can branch on specific error types. The tradeoff for this minimalism is that most of the actually useful usage documentation (pub/sub, request/reply, JetStream) lives one level up in the core module’s README rather than this package’s own, so someone landing on transport-node’s README alone gets an install command and one function signature, and has to already know to look at the core module’s docs for real usage.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search