---
title: Rooms
description: Learn about NoLag rooms for organizing topics and managing message namespaces.
---

# Rooms

Rooms provide logical namespaces for organizing topics within your application.

## What are Rooms?

Rooms are containers that group related topics together. They help organize your messaging structure and provide isolation between different contexts (e.g., different game lobbies, chat channels, or user spaces).

```typescript [TypeScript]
import { NoLag } from '@nolag/js-sdk'

const client = NoLag('your_access_token')
await client.connect()

// Set up app and room
const room = client.setApp('my-app').setRoom('game-lobby')

// Subscribe to topics within this room
room.subscribe('chat')
room.subscribe('player-updates')

// Messages are scoped to this room
room.emit('chat', { text: 'Hello lobby!' })
```
```python [Python]
from nolag import NoLag

client = NoLag('your_access_token')
await client.connect()

# Set up app and room
room = client.set_app('my-app').set_room('game-lobby')

# Subscribe to topics within this room
await room.subscribe('chat')
await room.subscribe('player-updates')

# Messages are scoped to this room
await room.emit('chat', {'text': 'Hello lobby!'})
```
```go [Go]
package main

import (
    "fmt"

    nolag "github.com/NoLagApp/nolag-go"
)

func main() {
    client := nolag.New("your_access_token")
    client.Connect()

    // Set up app and room
    room := client.SetApp("my-app").SetRoom("game-lobby")

    // Subscribe to topics within this room
    room.Subscribe("chat", func(data any, meta nolag.MessageMeta) {
        fmt.Println("Chat:", data)
    })
    room.Subscribe("player-updates", func(data any, meta nolag.MessageMeta) {
        fmt.Println("Player update:", data)
    })

    // Messages are scoped to this room
    room.Emit("chat", map[string]string{"text": "Hello lobby!"})
}
```

## Room Types

### Static Rooms

Defined in your app configuration via the dashboard. These rooms exist permanently and are ideal for fixed channels like `general`, `announcements`, or `support`.

### Dynamic Rooms

Created programmatically via the REST API for user-specific or session-specific namespaces. Dynamic rooms must be created server-side before clients can connect to them.

**Important:** Rooms cannot be created via WebSocket connections. You must first create the room and grant actor access using the REST API, then clients can connect via WebSocket.

#### Step 1: Create Room via API

```typescript [TypeScript]
import { NoLagApi } from '@nolag/js-sdk'

const api = new NoLagApi('your_api_key')

// Create a dynamic room - inherits topics from app config
const room = await api.rooms.create(appId, {
  name: `game-${gameId}`
})

console.log('Created room:', room.roomId)
```
```python [Python]
from nolag import NoLagApi, RoomCreate

api = NoLagApi('your_api_key')

# Create a dynamic room - inherits topics from app config
room = await api.rooms.create(app_id, RoomCreate(
    name=f'game-{game_id}'
))

print('Created room:', room.room_id)
```
```go [Go]
import nolag "github.com/NoLagApp/nolag-go"

api := nolag.NewAPI("your_api_key")

// Create a dynamic room - inherits topics from app config
room, err := api.Rooms.Create(ctx, appID, nolag.RoomCreate{
    Name: fmt.Sprintf("game-%s", gameID),
})

fmt.Println("Created room:", room.RoomID)
```

**Access control:** Actor access and permissions for rooms are managed through the [NoLag Dashboard or ACL configuration](/docs/concepts/acl), not programmatically via the SDK's REST client.

#### Step 2: Connect via WebSocket

```typescript [TypeScript]
// After room is created via API, connect to it via WebSocket
const gameRoom = client.setApp('my-app').setRoom(`game-${gameId}`)
gameRoom.subscribe('moves')
gameRoom.subscribe('chat')
```
```python [Python]
# After room is created via API, connect to it via WebSocket
game_room = client.set_app('my-app').set_room(f'game-{game_id}')
await game_room.subscribe('moves')
await game_room.subscribe('chat')
```
```go [Go]
// After room is created via API, connect to it via WebSocket
gameRoom := client.SetApp("my-app").SetRoom(fmt.Sprintf("game-%s", gameID))
gameRoom.Subscribe("moves", func(data any, meta nolag.MessageMeta) {
    fmt.Println("Move:", data)
})
gameRoom.Subscribe("chat", func(data any, meta nolag.MessageMeta) {
    fmt.Println("Chat:", data)
})
```

## Public vs Private Rooms

By default, rooms are **public**, meaning any actor with access to the app can connect to them. The moment you attach one or more actors to a room, that room becomes **private**. Only the attached actors can access it.

This means access control is built into room assignment rather than requiring separate permission rules. A few examples:

- **Notifications:** attach a user to their own room, and only they receive their notifications
- **Team chat:** attach team members to a shared room, and outsiders can't join
- **Device telemetry:** attach a device and its owner to a room, isolating the data stream

**No actors attached = public.** At least one actor attached = private. There is no toggle; the presence of actor assignments is what makes a room private.

## Topic Inheritance

Every room inherits the topics defined on its parent app. When you create a chat app with `messages` and `_typing` topics, every room in that app, whether static or dynamic, automatically has both topics available. You configure topics once at the app level and they scale to thousands of rooms.

## Topic Resolution

Topics within a room are namespaced automatically. When you subscribe to `chat` in room `game-lobby`, the full topic path is `my-app/game-lobby/chat`.

## Use Cases

- **Game lobbies** - Each game session gets its own room
- **Chat channels** - Separate rooms for different conversations
- **User spaces** - Private rooms for user-specific notifications
- **Multi-tenant apps** - Isolate data between organizations

## Next Steps

- [Learn about Topics](/docs/concepts/topics)
- [Set up Access Control](/docs/concepts/acl)
