Skip to content

Choose an API

beryl has one runtime and two ways to program it.

  • The channel layer (beryl/channel): Register one handler for each topic pattern. Each channel keeps private state and a server-side message type. The layer routes each event to the correct channel. Use this API by default.
  • Raw dispatch (beryl): Define one init and update pair for the socket system. beryl runs them separately for each connected socket. Match socket.Input values and return ordered effects. The channel layer uses this public core API.

Both APIs use the same socket processes, message format, presence, PubSub, connection limits, and rate limits. They support the same client features. The main difference is who routes topics.

If your app…Use
serves several topic namespaces on one socketChannel layer
is a port of a Phoenix Channels designChannel layer
wants colocated per-topic callbacks and stateChannel layer
consumes channels published by someone elseChannel layer
serves exactly one topic familyRaw dispatch
needs total control over routing and effect orderRaw dispatch
wants one model that spans every topic on a socketRaw dispatch
Channel layerRaw dispatch
Packageberylberyl
Entry pointchannel.child_spec(config, handlers:)beryl.child_spec(config, init:, update:)
RoutingHandler table, first matching pattern winsYour update, pattern matching on topics
Per-topic stateOne private value per joined topic, pruned on closeYour own model; you prune it in the Closed branch
Server-side messagesOne typed info type per channelOne typed msg type per socket
Side effectsOrdered Action(Active) lists scoped to the channel's topicsocket.Effect values naming any topic
Cleanupon_terminate per channelsocket.Closed(topic, reason) in your update
Cross-topic effectsExternal Sockets APIs onlyDirect, in any effect list
Routing code as channels growHandler list stays the same shapeMore topic families require more branches

Use channel handlers for separate topic features

Section titled “Use channel handlers for separate topic features”

To add a topic family to raw dispatch, extend the socket model and message union. Then add router branches. With the channel layer, add one handler to the list.

The layer gives each channel a private state type and server-side message type. Unrelated channels do not need a shared Model or Message. A library can publish a channel value for direct use.

One update sees all events for a socket. An event on one topic can broadcast on another topic in the same ordered effect list. Channel actions apply only to their own topic. Use the Sockets handle for cross-topic publishing.

Raw dispatch has fewer concepts: no handler table and no routing rules other than the ones you write.

Select one API for each socket endpoint. The channel layer owns the socket-level model and message type. This design lets each channel keep private state. Do not embed a channel system in a hand-written update. Different endpoints can use different APIs and share presence, PubSub, and group actors.

A later migration changes routing code only. Both APIs use the same wire format, join rules, presence payloads, and client code.