Go SDK
The official NoLag SDK for Go. Idiomatic Go client with goroutines and channels support.
Installation
go get github.com/NoLagApp/nolag-goQuick Start
package main
import (
"fmt"
"time"
nolag "github.com/NoLagApp/nolag-go"
)
func main() {
// Create client with your actor token
client := nolag.New("your-actor-token")
// Connect to NoLag
if err := client.Connect(); err != nil {
panic(err)
}
defer client.Close()
// Subscribe to a topic
err := client.Subscribe("my-topic", func(data any, meta nolag.MessageMeta) {
fmt.Printf("Received: %v\n", data)
})
if err != nil {
panic(err)
}
// Publish a message
if err := client.Emit("my-topic", map[string]any{"hello": "world"}); err != nil {
fmt.Printf("Emit failed: %v\n", err)
}
// Get actor ID assigned by server
fmt.Println("Actor ID:", client.ActorID())
// Keep running
time.Sleep(60 * time.Second)
}Configuration
import (
"time"
nolag "github.com/NoLagApp/nolag-go"
)
options := nolag.Options{
URL: "wss://broker.nolag.app/ws", // Custom broker URL
Reconnect: true, // Auto-reconnect (default: true)
ReconnectInterval: 5 * time.Second, // Reconnect interval (default: 5s)
MaxReconnectAttempts: 10, // Max attempts, 0 = infinite (default: 10)
HeartbeatInterval: 30 * time.Second, // Heartbeat interval, 0 to disable (default: 30s)
LoadBalance: true, // Enable load balancing (default: false)
LoadBalanceGroup: "workers", // Load balance group name
ActorTokenID: "custom-id", // Optional actor token identifier
Debug: true, // Enable debug logging (default: false)
}
client := nolag.New("your-actor-token", options)Subscribing to Topics
Subscribe, Unsubscribe, and all filter methods return error. Always check the returned error.
// Basic subscription — Subscribe returns an error
err := client.Subscribe("chat/messages", func(data any, meta nolag.MessageMeta) {
fmt.Printf("Message from %s: %v\n", meta.Sender, data)
})
if err != nil {
fmt.Printf("Subscribe failed: %v\n", err)
}
// With options (load balancing + filters)
loadBalance := true
err = client.Subscribe("tasks", handler, nolag.SubscribeOptions{
LoadBalance: &loadBalance,
LoadBalanceGroup: "workers",
Filters: []string{"priority:high", "region:us"},
})
if err != nil {
fmt.Printf("Subscribe failed: %v\n", err)
}
// Unsubscribe — also returns an error
if err := client.Unsubscribe("chat/messages"); err != nil {
fmt.Printf("Unsubscribe failed: %v\n", err)
}Publishing Messages
Emit returns an error. Use EmitOptions to set retain, echo, and filter targeting.
// Publish any data (maps, structs, strings, etc.) — Emit returns an error
if err := client.Emit("chat/messages", map[string]any{"text": "Hello!"}); err != nil {
fmt.Printf("Emit failed: %v\n", err)
}
// With options
echo := false
err := client.Emit("status", map[string]any{"online": true}, nolag.EmitOptions{
Retain: true, // Retain last message for new subscribers
Echo: &echo, // Don't receive this message back (default: true)
Filter: "room-1", // Target specific filter subscribers
})
if err != nil {
fmt.Printf("Emit failed: %v\n", err)
}Fluent API (SetApp / SetRoom)
The fluent API scopes all operations to an app/room pair. Topics are automatically prefixed, so room.Emit("messages", ...) publishes to "chat/general/messages".
// The fluent API scopes operations to an app/room.
// Topics are automatically prefixed with "app/room/".
room := client.SetApp("chat").SetRoom("general")
// Subscribe — topic becomes "chat/general/messages"
err := room.Subscribe("messages", func(data any, meta nolag.MessageMeta) {
fmt.Printf("Message: %v\n", data)
})
if err != nil {
fmt.Printf("Subscribe failed: %v\n", err)
}
// Emit — topic becomes "chat/general/messages"
if err := room.Emit("messages", map[string]any{"text": "Hello!"}); err != nil {
fmt.Printf("Emit failed: %v\n", err)
}
// Unsubscribe
room.Unsubscribe("messages")
// Filter management on a room
room.SetFilters("messages", []string{"priority:high"})
room.AddFilters("messages", []string{"priority:medium"})
room.RemoveFilters("messages", []string{"priority:high"})
// Event handlers on scoped topics
room.On("messages", func(args ...any) {
fmt.Println("Custom event on chat/general/messages")
})
room.Off("messages")
// Get the full topic prefix
fmt.Println(room.Prefix()) // "chat/general"Filter Management
Filters narrow which messages a subscriber receives. You can set filters at subscribe time or manage them dynamically with SetFilters, AddFilters, and RemoveFilters. On the publish side, use EmitOptions.Filter to target specific subscribers.
// Subscribe with initial filters
err := client.Subscribe("orders", handler, nolag.SubscribeOptions{
Filters: []string{"region:us", "status:pending"},
})
// Replace all filters for a topic (empty slice = receive all messages)
err = client.SetFilters("orders", []string{"region:eu", "status:shipped"})
// Add filters to existing set (deduplicates automatically)
err = client.AddFilters("orders", []string{"status:delivered"})
// Remove specific filters
err = client.RemoveFilters("orders", []string{"status:shipped"})
// Emit with a filter value — only subscribers with matching filter receive it
err = client.Emit("orders", orderData, nolag.EmitOptions{
Filter: "region:us",
})Connection Events
// Listen for connection events
client.On("connected", func(args ...any) {
fmt.Println("Connected!")
})
client.On("disconnected", func(args ...any) {
fmt.Println("Disconnected")
})
client.On("reconnecting", func(args ...any) {
attempt := args[0].(int)
fmt.Printf("Reconnecting... attempt %d\n", attempt)
})
client.On("error", func(args ...any) {
err := args[0].(error)
fmt.Printf("Error: %v\n", err)
})
client.On("presence", func(args ...any) {
topic := args[0].(string)
data := args[1]
fmt.Printf("Presence update on %s: %v\n", topic, data)
})
// Remove all handlers for an event
client.Off("error")
// Check connection status
if client.Status() == nolag.StatusConnected {
fmt.Println("We're connected!")
}
// Get the actor ID assigned by the server after authentication
fmt.Println("Actor ID:", client.ActorID())Presence
// Set your presence data
if err := client.SetPresence(map[string]any{
"status": "online",
"typing": false,
}); err != nil {
fmt.Printf("SetPresence failed: %v\n", err)
}
// Get presence of all actors in a topic
presenceList, err := client.GetPresence("chat/room-1")
if err == nil {
for _, actor := range presenceList {
fmt.Printf("%s (%s): %v (joined %s)\n",
actor.ActorTokenID,
actor.ActorType,
actor.Presence,
actor.JoinedAt,
)
}
}
// Listen for presence changes
client.On("presence", func(args ...any) {
topic := args[0].(string)
presence := args[1]
fmt.Printf("Presence update in %s: %v\n", topic, presence)
})Error Handling
package main
import (
"errors"
"fmt"
nolag "github.com/NoLagApp/nolag-go"
)
func main() {
client := nolag.New("your-actor-token")
// Handle errors via event
client.On("error", func(args ...any) {
if err, ok := args[0].(error); ok {
fmt.Printf("Error: %v\n", err)
}
})
// Connect with error handling
if err := client.Connect(); err != nil {
fmt.Printf("Connection failed: %v\n", err)
return
}
defer client.Close()
// All operations return errors
if err := client.Subscribe("topic", handler); err != nil {
// Check for specific error types
if errors.Is(err, nolag.ErrNotConnected) {
fmt.Println("Not connected!")
}
fmt.Printf("Subscribe failed: %v\n", err)
}
if err := client.Emit("topic", "data"); err != nil {
fmt.Printf("Emit failed: %v\n", err)
}
if err := client.Unsubscribe("topic"); err != nil {
fmt.Printf("Unsubscribe failed: %v\n", err)
}
if err := client.SetFilters("topic", []string{"filter1"}); err != nil {
fmt.Printf("SetFilters failed: %v\n", err)
}
}
// Sentinel errors available:
// nolag.ErrNotConnected — operation attempted while disconnected
// nolag.ErrAuthFailed — authentication failed
// nolag.ErrTimeout — operation timed outREST API Client
The SDK also includes a REST API client for managing apps, rooms, and actors:
package main
import (
"context"
"fmt"
nolag "github.com/NoLagApp/nolag-go"
)
func main() {
ctx := context.Background()
// Create API client with project-scoped API key
api := nolag.NewAPI("nlg_live_xxx.secret")
// List all apps in your project
apps, err := api.Apps.List(ctx, nil)
if err != nil {
panic(err)
}
fmt.Printf("Found %d apps\n", len(apps.Data))
// Create a new app
app, err := api.Apps.Create(ctx, nolag.AppCreate{
Name: "my-chat-app",
Description: "A real-time chat application",
})
if err != nil {
panic(err)
}
fmt.Printf("Created app: %s\n", app.AppID)
// Create a room in the app
room, err := api.Rooms.Create(ctx, app.AppID, nolag.RoomCreate{
Name: "general",
Slug: "general",
})
if err != nil {
panic(err)
}
fmt.Printf("Created room: %s\n", room.RoomID)
// Create an actor (save the access token!)
actor, err := api.Actors.Create(ctx, nolag.ActorCreate{
Name: "web-client",
ActorType: nolag.ActorDevice,
})
if err != nil {
panic(err)
}
fmt.Printf("Actor token: %s\n", actor.AccessToken)
}Load Balancing
Distribute messages across multiple subscribers:
// Enable load balancing per-subscription
loadBalance := true
err := client.Subscribe("tasks", processTask, nolag.SubscribeOptions{
LoadBalance: &loadBalance,
LoadBalanceGroup: "task-workers",
})
if err != nil {
fmt.Printf("Subscribe failed: %v\n", err)
}
// Or enable load balancing globally via connection options
client := nolag.New("your-actor-token", nolag.Options{
LoadBalance: true,
LoadBalanceGroup: "task-workers",
})Type Definitions
import nolag "github.com/NoLagApp/nolag-go"
// WebSocket Client
nolag.Client // The real-time messaging client
nolag.Options // Connection options (URL, Reconnect, LoadBalance, etc.)
nolag.SubscribeOptions // Subscription options (LoadBalance, Filters, etc.)
nolag.EmitOptions // Publish options (Retain, Echo, Filter)
nolag.App // Intermediate context from SetApp()
nolag.Room // Scoped pub/sub context from SetApp().SetRoom()
// Enums / Constants
nolag.ConnectionStatus // StatusDisconnected, StatusConnecting, StatusConnected, StatusReconnecting
nolag.ActorType // ActorDevice, ActorUser, ActorServer, ActorSession
nolag.QoS // QoSAtMostOnce, QoSAtLeastOnce, QoSExactlyOnce
// Sentinel Errors
nolag.ErrNotConnected // Not connected to broker
nolag.ErrAuthFailed // Authentication failed
nolag.ErrTimeout // Operation timed out
// Data types
nolag.MessageMeta // Message metadata (Sender, Timestamp, IsReplay, MsgID, Filter)
nolag.ActorPresence // Presence info (ActorTokenID, ActorType, Presence, JoinedAt)
nolag.MessageHandler // func(data any, meta MessageMeta)
nolag.EventHandler // func(args ...any)
// REST API Client
nolag.API // REST API client
nolag.APIOptions // API client options
nolag.NoLagAPIError // API error type
nolag.APIError // Raw API error details
// Resources
nolag.AppResource, nolag.AppCreate, nolag.AppUpdate
nolag.RoomResource, nolag.RoomCreate, nolag.RoomUpdate
nolag.ActorResource, nolag.ActorWithToken, nolag.ActorCreate, nolag.ActorUpdate
nolag.PaginatedApps, nolag.ListOptions
// WebRTC (uses pion/webrtc/v3)
nolag.WebRTCManager, nolag.WebRTCOptions, nolag.PeerState
nolag.WebRTCEvent, nolag.WebRTCEventHandler, nolag.TrackHandlerRequirements
- Go 1.21+
- github.com/gorilla/websocket v1.5.1
- github.com/vmihailenco/msgpack/v5 v5.4.1
- github.com/pion/webrtc/v3 v3.2.50 (WebRTC support)