Skip to content
Gerasimos (Makis) Maropoulos edited this page May 28, 2026 · 5 revisions

When to use this page

  • You want a subset of clients inside a namespace (a chat room, a game match, a document being collaboratively edited).
  • You need to broadcast to that subset without sending to every other client in the namespace.
  • You're listing room members or counting them.

What a room is

A room is a dynamic, named group of NSConns within a single namespace. Where namespaces are static (declared up-front), rooms are created and destroyed at runtime based on what clients want to join.

Property Namespace Room
Declared up-front
Lives as long as the server the last member
Scoped under the connection a namespace
Created by Conn.Connect NSConn.JoinRoom

A Room always belongs to exactly one NSConn. A client must be connected to the namespace before joining any of its rooms.

Joining a room

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

room, err := nsConn.JoinRoom(ctx, "lobby")
if err != nil {
    return err
}

Server-side handlers can deny the join by returning a non-nil error from OnRoomJoin:

neffos.Events{
    neffos.OnRoomJoin: func(c *neffos.NSConn, msg neffos.Message) error {
        if msg.Room == "vip" && !isVIP(c.Conn) {
            return errors.New("vip room is restricted")
        }
        return nil
    },
}

Sending to a room

Inside the namespace, broadcast to the room:

room.Emit("chat", []byte("hi everyone"))

This goes to every member of lobby in this namespace, including the sender. If you only want to send to the other members, use the namespace-level helper:

c.BroadcastOthers(neffos.Message{
    Room:  "lobby",
    Event: "chat",
    Body:  []byte("hi"),
})

(Namespace is filled automatically from the NSConn.)

Looking up rooms

room := nsConn.Room("lobby")     // *Room or nil
rooms := nsConn.Rooms()          // []*Room snapshot, slice-copied

Rooms() returns a snapshot of joined rooms taken under the lock — safe to iterate after the call returns.

Listing members

Neffos does not store an explicit "members of room X" index — for memory reasons, since most rooms have few members and most servers many rooms. To list members, combine Server.GetConnectionsByNamespace with NSConn.Room:

func MembersOfRoom(server *neffos.Server, namespace, room string) []string {
    var ids []string
    for id, ns := range server.GetConnectionsByNamespace(namespace) {
        if ns.Room(room) != nil {
            ids = append(ids, id)
        }
    }
    return ids
}

GetConnectionsByNamespace is not designed for hot paths — it materializes the entire map. Use it for occasional inspection or debugging.

Leaving a room

err := room.Leave(ctx)

Or leave every room at once:

err := nsConn.LeaveAll(ctx)

LeaveAll asks the server to leave each joined room sequentially under the lock and fires the local OnRoomLeave / OnRoomLeft events. If any leave fails the operation stops and the error propagates back.

Room lifecycle

sequenceDiagram
    autonumber
    participant C as Client (NSConn)
    participant S as Server (NSConn)
    C->>C: fire OnRoomJoin (local)
    C->>S: Ask(OnRoomJoin)
    S->>S: fire OnRoomJoin (remote)
    alt accepted
        S-->>C: empty reply (wait token)
        C->>C: rooms[name] = new Room
        C->>C: fire OnRoomJoined
        S->>S: rooms[name] = new Room
        S->>S: fire OnRoomJoined
    else denied
        S-->>C: error in msg.Err
    end
Loading

Rooms exist independently on each side of the wire: the client's Room and the server's Room are not the same object, just two sides of a shared name. Either side can refuse a join or a leave by returning an error from the corresponding OnRoomJoin / OnRoomLeave event.

See also

Clone this wiki locally