ChannelClient. Format behaviour
(channel_client v0.1.0)
Copy Markdown
Behaviour for pluggable wire formats.
A format owns both the structure of a frame and its encoding. The socket resolves a single format module at startup and routes every outbound/inbound frame through it, so formats are fully responsible for the bytes on the wire.
Built-in formats
ChannelClient.Formats.JSON- Phoenix Channels JSON encoding, protocol versions1.0.0(JSON objects) and2.0.0(JSON arrays), using any JSON library. This is the default.ChannelClient.Formats.ETF- Erlang External Term Format carrying the protocol v2 array shape (term_to_binary/binary_to_term). Zero dependencies; ideal for Elixir/Erlang-to-Elixir services. Decoding uses:safemode, so hostile peers cannot inject atoms.ChannelClient.Formats.TOON- compact text format (Token-Oriented Object Notation) delegating to a codec library such astoon_ex(default moduleToon, not a hard dependency).ChannelClient.Formats.BTOON- binary variant of TOON (default codec moduleBToon, not a hard dependency).
Selecting a format
# Default: JSON v2 with Jason
ChannelClient.Socket.start_link(url: "ws://...")
# ETF
ChannelClient.Socket.start_link(url: "ws://...", format: :etf)
# TOON / BTOON (requires a TOON codec library, e.g. toon_ex)
ChannelClient.Socket.start_link(url: "ws://...", format: :toon)
ChannelClient.Socket.start_link(url: "ws://...", format: :btoon)
# JSON v1 with Poison
ChannelClient.Socket.start_link(
url: "ws://...",
format: {ChannelClient.Formats.JSON, version: "1.0.0", json_library: Poison}
)
# Custom module
ChannelClient.Socket.start_link(url: "ws://...", format: MyApp.MsgPack)The legacy options still work and remain the default path:
vsn: "1.0.0" selects the JSON protocol version and json_library:
(alias serializer:) swaps the JSON codec.
Writing a custom format
defmodule MyApp.MsgPack do
@behaviour ChannelClient.Format
@impl true
def encode!(%ChannelClient.Message{} = msg, _opts) do
Msgpax.pack!([msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload])
end
@impl true
def decode!(payload, _opts) when is_binary(payload) do
[join_ref, ref, topic, event, payload | _] = Msgpax.unpack!(payload)
%ChannelClient.Message{join_ref: join_ref, ref: ref, topic: topic, event: event, payload: payload}
end
endencode!/2 may raise on unencodable payloads — the socket converts that
into {:error, reason} for sync callers and logs+drops for async pushes.
decode!/2 may raise on malformed frames — those are logged and dropped
instead of crashing the socket. Prefer returning tagged tuples from your
own code only if you want custom error terms.
Summary
Callbacks
Parses raw transport payload back into a %Message{}. May raise on
malformed input.
Serializes a message into wire bytes/iodata. May raise on invalid payloads.
Validates and normalizes format options. Invoked once at socket startup; return the keyword list to be handed back to every callback. Optional — defaults to returning opts unchanged.
Functions
Safe decode wrapper used by the socket: never raises.
Safe encode wrapper used by the socket: never raises.
Resolves a :format option value into {module, opts}.
Types
Callbacks
@callback decode!(term(), opts()) :: ChannelClient.Message.t() | no_return()
Parses raw transport payload back into a %Message{}. May raise on
malformed input.
@callback encode!(ChannelClient.Message.t(), opts()) :: iodata() | no_return()
Serializes a message into wire bytes/iodata. May raise on invalid payloads.
Validates and normalizes format options. Invoked once at socket startup; return the keyword list to be handed back to every callback. Optional — defaults to returning opts unchanged.
Functions
@spec decode(module(), term(), opts()) :: {:ok, ChannelClient.Message.t()} | {:error, term()}
Safe decode wrapper used by the socket: never raises.
@spec encode(module(), ChannelClient.Message.t(), opts()) :: {:ok, iodata()} | {:error, term()}
Safe encode wrapper used by the socket: never raises.
Resolves a :format option value into {module, opts}.
Accepts nil (falls back to legacy vsn/json_library options), built-in
shorthands (:json, :etf), bare modules, or {module, opts} tuples.
Raises ArgumentError on unusable values so misconfiguration fails at
startup.