---
title: Getting Started
description: Get started with NoLag in 5 minutes. Learn how to connect, subscribe to topics, and publish messages.
---

# Getting Started

Get up and running with NoLag in under 5 minutes.

## Prerequisites

- A NoLag account ([sign up free](https://portal.nolag.app))
- Node.js 18+ (for the JavaScript SDK)

You create your app and access token in Step 2, so nothing else is needed up front.

## Step 1: Install the SDK

Install the NoLag SDK for your preferred language:

```typescript [npm]
npm install @nolag/js-sdk
```
```python [pip]
pip install nolag
```
```go [go get]
go get github.com/NoLagApp/go-sdk
```

## Step 2: Create an App, a Room, and an Access Token

You need three things:

- an **app**, a container for your rooms and topics
- a **room** inside that app, the namespace your topics live in
- an **actor**, a client identity whose access token authenticates the connection

**In the dashboard:**

1. Log in to the [NoLag Dashboard](https://portal.nolag.app) and create a project.
2. Create an app inside the project.
3. Add a room to the app, and note its slug.
4. Open the **Actors** section, create an actor, and copy its access token.

**Or over the REST API.** This is the path to use from a script or an AI agent
that sets everything up on its own. The only bootstrap secret is a project-scoped
[API key](/docs/authentication#api-keys), created once in the dashboard;
everything else is an API call:

```bash [Terminal]
# 1. Create an app. Read appId and slug back out of the response:
#    the returned slug is not the one you sent (see the note below).
curl -X POST https://api.nolag.app/v1/apps \
  -H "Authorization: Bearer nlg_live_xxx.secret" \
  -H "Content-Type: application/json" \
  -d '{"name":"My App","slug":"my-app","topics":["messages"]}'
# => {"appId":"019fd987-9dee-75cb-97a7-39d71957ca23","slug":"my-app-a3f9",...}

# 2. Create a room in that app, using the appId from step 1.
#    Clients cannot subscribe to a room that does not exist yet.
curl -X POST https://api.nolag.app/v1/apps/019fd987-9dee-75cb-97a7-39d71957ca23/rooms \
  -H "Authorization: Bearer nlg_live_xxx.secret" \
  -H "Content-Type: application/json" \
  -d '{"name":"General","slug":"general","topics":["messages"]}'

# 3. Create an actor, then copy accessToken from the response
curl -X POST https://api.nolag.app/v1/actors \
  -H "Authorization: Bearer nlg_live_xxx.secret" \
  -H "Content-Type: application/json" \
  -d '{"name":"Web Client","actorType":"device"}'
```

**Save the token now.** An actor's access token is only returned when the actor is
created. It is never shown again.

**App slugs always get a random suffix.** NoLag appends four random characters to
every app slug to keep it unique within the project, so a requested slug of
`my-app` comes back as something like `my-app-a3f9`. Read the `slug` field from the
create response and pass that value to `setApp()`. Room slugs are different: they
are stored exactly as you supply them.

**Rooms must exist before you connect.** Subscribing to a room that has not been
created returns an `unknown_topic` error instead of delivering messages. If you
skip the error handler in Step 3, this looks like total silence: `connect()`,
`subscribe()`, and `emit()` all appear to succeed and no message ever arrives.

See the [REST API Reference](/docs/api-reference) for the full app, room, and actor endpoints.

## Step 3: Connect to NoLag

Attach an error handler before you connect. Subscribe and publish calls do not
throw: the broker reports problems such as an unknown topic or a permission
refusal on the `error` event, so without a handler a misconfigured app simply
goes quiet.

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

// Create client and connect
const client = NoLag('your_access_token')

// Attach this first: server-side errors arrive here, not as thrown exceptions
client.on('error', (err) => {
  console.error(`NoLag ${err.error} (${err.code}) on ${err.topic}: ${err.hint}`)
})

await client.connect()

console.log('Connected to NoLag!')
```
```python [Python]
from nolag import NoLag

# Create client and connect
client = NoLag('your_access_token')

# Attach this first: server-side errors arrive here, not as raised exceptions
def handle_error(err):
    print(f'NoLag error: {err}')

client.on('error', handle_error)

await client.connect()

print('Connected to NoLag!')
```
```go [Go]
package main

import (
    "fmt"
    "log"

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

func main() {
    // Create client and connect
    client := nolag.New("your_access_token")

    // Attach this first: server-side errors arrive here, not as returned errors
    client.OnError(func(err *nolag.ServerError) {
        log.Printf("NoLag %s (%d) on %s: %s", err.Name, err.Code, err.Topic, err.Hint)
    })

    if err := client.Connect(); err != nil {
        log.Fatal(err)
    }

    fmt.Println("Connected to NoLag!")
}
```

## Step 4: Subscribe to a Topic

Topics are channels for messages. Subscribe to receive messages published to a topic.

Pass the app slug exactly as the create response returned it, suffix included, and
the slug of a room that already exists:

```typescript [TypeScript]
// Set up app and room, then subscribe.
// 'my-app-a3f9' is the slug returned when the app was created.
const room = client.setApp('my-app-a3f9').setRoom('general')
room.subscribe('messages')

// Listen for messages
room.on('messages', (data) => {
  console.log('Received:', data)
})
```
```python [Python]
# Set up app and room, then subscribe.
# 'my-app-a3f9' is the slug returned when the app was created.
room = client.set_app('my-app-a3f9').set_room('general')
await room.subscribe('messages')

# Listen for messages
def handle_message(data, meta):
    print('Received:', data)

room.on('messages', handle_message)
```
```go [Go]
import "fmt"

// Set up app and room, then subscribe with handler.
// "my-app-a3f9" is the slug returned when the app was created.
room := client.SetApp("my-app-a3f9").SetRoom("general")
room.Subscribe("messages", func(data any, meta nolag.MessageMeta) {
    fmt.Println("Received:", data)
})
```

## Step 5: Publish a Message

Publish messages to a topic for all subscribers to receive:

```typescript [TypeScript]
// Publish a message
room.emit('messages', {
  text: 'Hello, World!',
  sender: 'user-123',
  timestamp: Date.now()
})
```
```python [Python]
# Publish a message
await room.emit('messages', {
    'text': 'Hello, World!',
    'sender': 'user-123',
    'timestamp': time.time()
})
```
```go [Go]
// Publish a message
room.Emit("messages", map[string]any{
    "text":      "Hello, World!",
    "sender":    "user-123",
    "timestamp": time.Now().Unix(),
})
```

## Complete Example

Here's a complete example in your preferred language. It assumes the app, room, and
actor from Step 2 already exist.

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

const client = NoLag('your_access_token')

// Surface broker errors: subscribe and emit never throw
client.on('error', (err) => {
  console.error(`NoLag ${err.error} (${err.code}) on ${err.topic}: ${err.hint}`)
})

await client.connect()

// Use the app slug returned by the create call, and a room that exists
const room = client.setApp('my-app-a3f9').setRoom('general')

// Subscribe to a topic
room.subscribe('messages')

// Listen for messages
room.on('messages', (data) => {
  console.log('Received:', data)
})

// Publish a message
room.emit('messages', { text: 'Hello, World!' })
```
```python [Python]
from nolag import NoLag

client = NoLag('your_access_token')

# Surface broker errors: subscribe and emit never raise
def handle_error(err):
    print(f'NoLag error: {err}')

client.on('error', handle_error)

await client.connect()

# Use the app slug returned by the create call, and a room that exists
room = client.set_app('my-app-a3f9').set_room('general')

# Subscribe to a topic
await room.subscribe('messages')

# Listen for messages
def handle_message(data, meta):
    print('Received:', data)

room.on('messages', handle_message)

# Publish a message
await room.emit('messages', {'text': 'Hello, World!'})
```
```go [Go]
package main

import (
    "fmt"
    "log"

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

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

    // Surface broker errors: Subscribe and Emit do not return them
    client.OnError(func(err *nolag.ServerError) {
        log.Printf("NoLag %s (%d) on %s: %s", err.Name, err.Code, err.Topic, err.Hint)
    })

    if err := client.Connect(); err != nil {
        log.Fatal(err)
    }

    // Use the app slug returned by the create call, and a room that exists
    room := client.SetApp("my-app-a3f9").SetRoom("general")

    // Subscribe to a topic with message handler
    room.Subscribe("messages", func(data any, meta nolag.MessageMeta) {
        fmt.Println("Received:", data)
    })

    // Publish a message
    room.Emit("messages", map[string]string{"text": "Hello, World!"})
}
```

## Troubleshooting

**Nothing arrives, and nothing errors.** Almost always the room does not exist, or
the app slug is missing its random suffix. Attach the `error` handler from Step 3
and look for `unknown_topic`, then check the slug against
`GET /apps` and the room against `GET /apps/{appId}/rooms`.

**`unknown_topic` on a topic you did create.** Topics resolve as
`app-slug/room-slug/topic-name`. Confirm the topic is listed on the room, not only
on the app.

See the [Error Reference](/docs/api-reference/errors) for the full list of codes.

## Next Steps

- [Learn about Topics & Pub/Sub](/docs/concepts/topics)
- [Understand Presence Tracking](/docs/concepts/presence)
- [Configure Quality of Service](/docs/concepts/qos)
- [Full JavaScript SDK Reference](/docs/sdks/javascript)
