Liking cljdoc? Tell your friends :D

nats-cljc

A Clojure/ClojureScript library that exposes NATS messaging under a single, portable .cljc API. The same consumer code is intended to compile and run unchanged across three platforms — the JVM, the browser, and Node.

Language

Platforms & transport

Platform: One of the three execution environments nats-cljc targets: JVM, browser, or Node. Used to say where portable code runs and which transport applies. Avoid: runtime, target, host, environment

Transport: The wire binding a connection uses to reach a NATS server: TCP on the JVM, WebSocket on the browser and Node. WebSocket is mandatory for any ClojureScript platform, so a reachable server must have its websocket listener enabled. Avoid: protocol, link, channel

Async surface

Promise: The value returned by every one-shot operation (connect, request, publish-with-ack, …): a single eventual result-or-error that the caller awaits. The portable currency for "this finishes once." Avoid: future, deferred

Handler: The function the caller supplies to receive delivered messages, one call per message, for as long as a subscription is active. The portable currency for "this happens many times." Avoid: callback, listener, subscriber, consumer (consumer is reserved for its JetStream meaning)

Connection

Connection: The value connect returns: a native client wrapped together with a default codec and options, and the thing every publish, subscribe, and request flows through. Draining or closing it ends all of its subscriptions. Avoid: client, session

Status event: A normalized connection-lifecycle notification delivered to an :on-status handler, identical in shape on every platform. Canonical :types: :connected, :disconnected, :reconnecting, :reconnected, :closed, :error, :lame-duck, :servers-changed. (:slow-consumer is not here: it is inherently per-subscription, so it is an Error :type routed to the subscription's :on-error, keeping every lifecycle :on-status event a bare connection-level {:type ...}.) The contract normalizes shape, not cadence: each delivered lifecycle event is a bare {:type ...} map drawn from the canonical set, but the count, ordering, and trigger conditions follow each underlying client's native strategy and may differ per platform (see ADR 0006). The :error event is the lone exception to bareness: it carries the offending Error under an :error key ({:type :error :error <ex-info>}), so a consumer dispatches uniformly on (:type ev) and, for :error, reads the canonical error :type with (:type (ex-data (:error ev))). Known divergences: a single connection loss yields one :reconnecting on the JVM (synthesized) but one per dial attempt on Node/browser (nats.js' native signal); :servers-changed fires only when genuinely new servers are gossiped on the JVM, but on essentially every server INFO on Node/browser. Portable consumers should treat each :type as an edge to react to, not a counter. Avoid: connection listener, notification

Reconnect: The client's automatic re-establishment of a dropped connection, configured with the :reconnect {:max :wait-ms :jitter-ms} connect-option. :max is a non-negative attempt count with two sentinels shared by both underlying clients: 0 disables reconnection, -1 is unlimited; :wait-ms/:jitter-ms set the per-attempt delay and its random spread. Any absent key leaves the native client's own default in place — and those defaults differ (JVM 60 attempts, Node/browser 10), so omitting :max does not give identical retry behavior across platforms. Avoid: retry, redial

Error: A failure surfaced as an ex-info carrying a canonical :type and structured data, identical in shape on every platform (so portable code reads (:type (ex-data e)) rather than branching on host exception types). Canonical :types: :timeout, :no-responders, :connect-failed, :connection-closed, :permissions-violation, :codec-error, :max-payload-exceeded, :protocol-error, :drained, :slow-consumer, :auth-invalid. :auth-invalid is client-side credential validation failing before any dial (e.g. an nkey that does not match its seed), distinct from :connect-failed (the server-side connect attempt failed); it rejects the connect promise. One-shot operations reject their promise with it. Async failures reach a sink instead: a throwing handler or a decode failure reaches the subscription's :on-error if one is set, else the connection's :on-status as an :error event; :slow-consumer — being per-subscription — reaches the subscription's :on-error only (and is silently dropped when none is set); while connection-level :permissions-violation and :protocol-error reach :on-status as an :error event only, never a per-subscription override. The override is strict: when a subscription sets :on-error, only it fires — never both it and :on-status. :on-error receives the bare ex-info, so portable code reads (:type (ex-data e)) exactly as on the one-shot reject path; the connection-level :error event wraps that ex-info under :error (see Status event). A thrown handler value is passed through unchanged and carries no canonical :type — it is the consumer's own exception, not a normalized NATS failure. Avoid: failure, fault (a bare host exception is what we normalize into an Error)

Validation error: A caller-misuse failure — a malformed argument caught before any native NATS call — surfaced as an ex-info carrying a :type from a separate set, distinct from the canonical Error set and sharing only its shape. Current :types: :invalid-header (a header name or value that is not a printable-ASCII token), :invalid-max (a max argument outside the integer range its operation accepts — the 2147483647 JVM int cap on both legs for unsubscribe/blocking subscribe, with :reconnect additionally allowing the 0/-1 sentinels), :invalid-max-pending (a non-positive subscribe :max-pending), :no-reply-subject (reply to a message that has no :reply), and :invalid-capacity (a non-positive blocking-subscribe capacity). Raised through the operation's own channel — a synchronous operation throws it, the one promise-returning operation that validates (connect) rejects its promise — and it never reaches an async sink (:on-error/:on-status), which is exclusively the Error model's channel (ADR 0006, ADR 0015). Where the canonical Error :types are operational conditions a consumer dispatches on in production, a validation :type is a programmer error to fix: the set is diagnostic-first and open — new guards add new :types — so consumers should not assume it exhaustive. Avoid: error (reserve Error for a normalized NATS failure); argument exception, bad-input fault

Messaging

Message: The unit published to and delivered from NATS: a subject, its data, optional headers, and an optional reply subject. Delivered and published as a plain-keyword map {:subject :data :headers :reply}, where :data is the decoded value. Avoid: event, packet

Subject: A dot-delimited token string naming where a message is published and what a subscription listens to; supports the wildcards * (exactly one token) and > (one or more trailing tokens). The string is canonical; a builder helper composes one from parts. Avoid: topic, address, queue (a queue group is a different thing)

Headers: Optional named string values carried by a message, HTTP-style: case-sensitive string names, each mapping to one or more string values. Delivered as name → vector-of-strings; a scalar value is accepted on publish and normalized to a one-element vector. Surrounding whitespace is insignificant and stripped on delivery. Avoid: properties, attributes, metadata

Data: The decoded value a message carries — ordinary Clojure data, once a codec has been applied. The raw wire form is "bytes" and is never called data. Avoid: payload, body, content

Codec: The pluggable component that converts between Clojure values and the bytes on the wire. A connection has a default codec; any publish/subscribe/request/reply may override it. Avoid: serializer, serde, marshaller (encoder/decoder are the two directions within a codec, not synonyms for it)

Subscription: The active interest in a subject that subscribe returns synchronously, delivering each matching message to its handler until unsubscribed or drained. May belong to a queue group. Avoid: subscriber, listener

Pull subscription: The interest a JVM-only blocking subscribe returns: a handle with no handler, drained one message at a time by take-message (which blocks up to a timeout) rather than pushed to. Backed by a bounded buffer that backpressures the producer. The synchronous counterpart to the portable push Handler model, and the reason the blocking layer exists. Avoid: poll loop, pull consumer (consumer is reserved for its JetStream meaning), queue

Unsubscribe: Ending a subscription abruptly: the server is told to stop and any not-yet-delivered messages are dropped, returning nil synchronously. Contrast draining a subscription, which delivers the already-buffered messages first and is awaitable — both end a subscription, but unsubscribe discards the backlog where drain flushes it. An optional max makes the subscription auto-unsubscribe once it has received that many messages over its lifetime (counted from subscription start; messages already delivered past the limit are never recalled). Avoid: cancel, stop, remove (a Subscription is unsubscribed; a Connection is closed)

Queue group: A named set of subscriptions to the same subject among which the server load-balances, so each message reaches exactly one member. Selected with the :queue option on subscribe. Avoid: consumer group, worker pool

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close