ADR 0004 fixed that codecs are pluggable, EDN is the default, and Transit/JSON are opt-in behind a registry. This ADR pins the shape of that extension point and how a codec reference resolves.
A codec is a defprotocol ICodec with -encode/-decode, living in nats-cljc.codec. The three built-ins (:edn/:string/:bytes) are records implementing it, held in a defonce registry atom keyed by keyword and seeded at load. Opt-in codecs self-register from their own namespace via the public register! — (require '[nats-cljc.codec.transit]) is what makes :transit resolvable. A custom codec is any ICodec instance, accepted wherever a keyword is (connection :codec, or a per-call :codec on publish/subscribe/request/reply).
encode/decode stay the public seam. They resolve-codec the reference before the try — an ICodec instance passes through; a keyword is looked up in the registry — so a resolution failure surfaces as a :codec-error with no :op, structurally distinct from an encode/decode failure rather than told apart by inspecting ex-data. The protocol call then runs inside a try/catch that normalizes any failure to ex-info :type :codec-error always carrying the operation under :op (:encode/:decode) and a stable :codec id — the registry keyword, or the sentinel :custom for an instance codec, never the live object or its class-hash toString. An already-:codec-error raised inside the call (the :bytes guard, or a codec that nests codec/encode/decode) is re-stamped with the outer op/codec rather than rethrown verbatim, so a caller matching on :op/:codec sees that operation, not an inner one; its cause — never the codec-error itself — is chained, so :codec-error is never nested in :codec-error.
A keyword that misses the registry also throws :codec-error, distinguished only by ex-data, not by a new canonical type: a known opt-in keyword carries :require '<ns> and an actionable "require this namespace" message; a genuinely unknown keyword carries just :codec.
{:encode fn :decode fn} map instead of a protocol — lighter for a custom-codec author (a literal map, no reify), but "implement the encode/decode protocol" (ADR 0004's own words) reads as a Clojure protocol, and a protocol gives the polymorphic dispatch the code-quality follow-up (nts-01ksxx84gzkf, Finding 2) wanted in place of the original case. The two-function-wrapper convenience was deliberately not added on top — reify ICodec is ≤5 lines and a second construction path is speculative until proven common.:type :codec-not-loaded for the unloaded-opt-in case — rejected: the canonical :type set (CONTEXT.md, ADR 0006) is public contract, and "the codec couldn't be resolved" is still a codec error. The unloaded/unknown nuance rides in ex-data (:require) instead of expanding the vocabulary.ICodec and its method names are public API the moment a consumer ships a custom codec — changing them is a breaking change.defonce means re-evaluating nats-cljc.codec in a REPL preserves prior registrations (stable for opt-in loading); a redefined built-in needs an explicit registry reset to take effect.require lets one deftest walk unloaded → require → round-trip in guaranteed order); the cljs legs load the opt-in namespace at compile time and test round-trip only.:on-error / the :on-status :error sink) is the normalized-error model's concern (ADR 0006), not this ADR's — here the contract is only that the ex-info is shaped :codec-error.codec/Prepared holding the resolved ICodec and the stable id) and stores it on the connection — steady-state publish/subscribe/request/reply then encode/decode with no registry deref. The keyword path stays for per-call :codec overrides (the rare case). One consequence: an unresolvable default codec (an unloaded opt-in keyword, a typo) now rejects the connect promise rather than surfacing lazily on the first publish; a per-call override still fails at the call that uses it.Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |