Groups
Groups are server-side named collections of topics. They let you broadcast a single event to many topics at once without tracking subscriptions yourself — similar to Socket.IO rooms or SignalR groups, but adapted to beryl's topic/channel model.
Starting the groups actor
Section titled “Starting the groups actor”import beryl/group
let assert Ok(groups) = group.start()group.start() returns Result(Groups, GroupError). The only failure case is StartFailed — an OTP actor spawn failure.
Creating and deleting groups
Section titled “Creating and deleting groups”// Create a grouplet assert Ok(Nil) = group.create(groups, "team:engineering")
// Error: AlreadyExists if the name is takencase group.create(groups, "team:engineering") { Ok(Nil) -> Nil Error(group.AlreadyExists) -> Nil // already there Error(_) -> Nil}
// Delete a group (removes it and all its topic memberships)let assert Ok(Nil) = group.delete(groups, "team:engineering")
// Error: NotFound if it doesn't existcase group.delete(groups, "team:gone") { Ok(Nil) -> Nil Error(group.NotFound) -> Nil Error(_) -> Nil}Adding and removing topics
Section titled “Adding and removing topics”Topics are plain strings that match existing channel topics. Groups do not validate that a topic has any subscribers — they are just sets of strings.
let assert Ok(Nil) = group.add(groups, "team:engineering", "room:frontend")let assert Ok(Nil) = group.add(groups, "team:engineering", "room:backend")let assert Ok(Nil) = group.add(groups, "team:engineering", "room:infra")
// Remove one topiclet assert Ok(Nil) = group.remove(groups, "team:engineering", "room:infra")
// Both add and remove return Error(NotFound) if the group doesn't existAdding the same topic twice is a no-op (topics are stored in a set).
Inspecting groups
Section titled “Inspecting groups”// List all topics in a groupcase group.topics(groups, "team:engineering") { Ok(topic_set) -> set.to_list(topic_set) // ["room:frontend", "room:backend"] Error(group.NotFound) -> [] Error(_) -> []}
// List all group nameslet names = group.list_groups(groups) // ["team:engineering", "team:design"]Broadcasting to a group
Section titled “Broadcasting to a group”group.broadcast sends an event to every topic in the named group using beryl.broadcast internally. It is fire-and-forget: the return type is Nil, not Result. If the named group does not exist, the call silently does nothing.
group.broadcast( groups, channels, "team:engineering", "deploy_started", json.object([#("env", json.string("production"))]),)This is equivalent to calling beryl.broadcast on each topic in the group in sequence.
Error reference
Section titled “Error reference”| Error | When |
|---|---|
AlreadyExists | create called for a name already in use |
NotFound | delete, add, remove, or topics called for an unknown group name |
StartFailed | group.start() — the OTP actor failed to initialize |
Full example: team rooms
Section titled “Full example: team rooms”import berylimport beryl/groupimport gleam/json
// At startuplet assert Ok(groups) = group.start()let assert Ok(Nil) = group.create(groups, "team:eng")let assert Ok(Nil) = group.add(groups, "team:eng", "room:frontend")let assert Ok(Nil) = group.add(groups, "team:eng", "room:backend")
// Later: broadcast deployment notice to all engineering roomsgroup.broadcast( groups, channels, "team:eng", "deploy_complete", json.object([ #("version", json.string("1.4.2")), #("deployed_by", json.string("ci")), ]),)
// When a team is disbandedlet assert Ok(Nil) = group.delete(groups, "team:eng")Using groups with the supervisor
Section titled “Using groups with the supervisor”When using beryl/supervisor, set groups: True in SupervisedConfig and access the groups handle from the returned SupervisedChannels:
import beryl/supervisorimport gleam/option.{None}
let config = supervisor.SupervisedConfig( channels: beryl.default_config(), presence: None, groups: True,)let assert Ok(supervised) = supervisor.start(config)
// supervised.groups is Option(group.Groups)case supervised.groups { Some(g) -> group.broadcast(g, supervised.channels, "team:eng", "alert", payload) None -> Nil}See the Supervision guide for details on the supervised startup pattern.