---
title: "Quality of Service (QoS)"
description: "Learn about NoLag QoS levels: at-most-once, at-least-once, and exactly-once message delivery guarantees."
---

# Quality of Service (QoS)

Choose the right delivery guarantee for your messages. NoLag supports three QoS levels for different reliability requirements.

## QoS Levels

Quality of Service determines how NoLag handles message delivery. Different use cases require different guarantees:

| Level | Name | Guarantee | Use Case |
| --- | --- | --- | --- |
| `0` | At-most-once | Fire and forget | Telemetry, status updates |
| `1` | At-least-once | Acknowledged delivery | Notifications, chat messages |
| `2` | Exactly-once | Transactional | Payments, critical events |

## QoS 0: At-most-once

Messages are sent once with no acknowledgment. The fastest option, but messages may be lost if the connection drops.

```typescript [TypeScript]
// Fire and forget - no delivery confirmation
client.emit('sensors/temperature', { temperature: 72.5 }, { qos: 0 })
```
```python [Python]
# Fire and forget - no delivery confirmation
await client.emit('sensors/temperature', {'temperature': 72.5}, EmitOptions(qos=QoS.AT_MOST_ONCE))
```
```go [Go]
// Fire and forget - no delivery confirmation
qos := nolag.QoSAtMostOnce
client.Emit("sensors/temperature", map[string]any{"temperature": 72.5}, nolag.EmitOptions{QoS: &qos})
```

**Best for:**

- Sensor data that updates frequently
- Live location updates
- Typing indicators
- Mouse cursor positions

## QoS 1: At-least-once

Messages are acknowledged by the broker. If no acknowledgment is received, the message is resent. Messages may be delivered more than once.

```typescript [TypeScript]
// Acknowledged delivery
client.emit('notifications', { type: 'alert', message: 'You have a new message' }, { qos: 1 })
```
```python [Python]
# Acknowledged delivery
await client.emit('notifications', {'type': 'alert', 'message': 'You have a new message'}, EmitOptions(qos=QoS.AT_LEAST_ONCE))
```
```go [Go]
// Acknowledged delivery
qos := nolag.QoSAtLeastOnce
client.Emit("notifications", map[string]any{"type": "alert", "message": "You have a new message"}, nolag.EmitOptions{QoS: &qos})
```

**Best for:**

- Chat messages
- Notifications
- Event broadcasts
- Most real-time applications

## QoS 2: Exactly-once

Messages are delivered exactly once using a four-step handshake. The most reliable but slowest option.

```typescript [TypeScript]
// Exactly-once delivery
client.emit('orders/status', { orderId: '12345', status: 'completed', amount: 99.99 }, { qos: 2 })
```
```python [Python]
# Exactly-once delivery
await client.emit('orders/status', {'orderId': '12345', 'status': 'completed', 'amount': 99.99}, EmitOptions(qos=QoS.EXACTLY_ONCE))
```
```go [Go]
// Exactly-once delivery
qos := nolag.QoSExactlyOnce
client.Emit("orders/status", map[string]any{"orderId": "12345", "status": "completed", "amount": 99.99}, nolag.EmitOptions{QoS: &qos})
```

**Best for:**

- Financial transactions
- Order processing
- Critical state changes
- Inventory updates

## Choosing the Right QoS

### Decision Guide

- **Can you tolerate lost messages?** &rarr; Use QoS 0
- **Need delivery confirmation?** &rarr; Use QoS 1
- **Can't tolerate duplicates?** &rarr; Use QoS 2
- **Not sure?** &rarr; Start with QoS 1 (recommended default)

## Handling Duplicates (QoS 1)

When using QoS 1, implement idempotency to handle potential duplicate messages:

```typescript [TypeScript]
const processedMessages = new Set()

client.on('orders/new', (data, meta) => {
  // Use a unique identifier from your data
  const messageId = data.orderId

  // Skip if already processed
  if (processedMessages.has(messageId)) {
    return
  }

  // Process the message
  processOrder(data)

  // Mark as processed
  processedMessages.add(messageId)

  // Clean up old IDs periodically
  if (processedMessages.size > 10000) {
    const entries = Array.from(processedMessages)
    entries.slice(0, 5000).forEach(id => processedMessages.delete(id))
  }
})
```
```python [Python]
processed_messages = set()

def handle_order(data, meta):
    # Use a unique identifier from your data
    message_id = data['orderId']

    # Skip if already processed
    if message_id in processed_messages:
        return

    # Process the message
    process_order(data)

    # Mark as processed
    processed_messages.add(message_id)

    # Clean up old IDs periodically
    if len(processed_messages) > 10000:
        to_remove = list(processed_messages)[:5000]
        for id in to_remove:
            processed_messages.discard(id)

client.on('orders/new', handle_order)
```
```go [Go]
var (
    processedMessages = make(map[string]bool)
    mu                sync.Mutex
)

client.Subscribe("orders/new", func(data any, meta nolag.MessageMeta) {
    msg, _ := data.(map[string]any)
    messageId, _ := msg["orderId"].(string)

    mu.Lock()
    defer mu.Unlock()

    // Skip if already processed
    if processedMessages[messageId] {
        return
    }

    // Process the message
    processOrder(msg)

    // Mark as processed
    processedMessages[messageId] = true

    // Clean up old IDs periodically
    if len(processedMessages) > 10000 {
        i := 0
        for id := range processedMessages {
            if i >= 5000 {
                break
            }
            delete(processedMessages, id)
            i++
        }
    }
})
```

## Default QoS

You can set a default QoS level for all messages:

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

// Set default QoS for all messages
const client = NoLag('your_access_token', {
  qos: 1  // Default QoS level
})

await client.connect()

// Override per-message
client.emit('topic', data, { qos: 2 })
```
```python [Python]
from nolag import NoLag, NoLagOptions, EmitOptions, QoS

# Set default QoS for all messages
client = NoLag('your_access_token', NoLagOptions(
    qos=QoS.AT_LEAST_ONCE  # Default QoS level
))

await client.connect()

# Override per-message
await client.emit('topic', data, EmitOptions(qos=QoS.EXACTLY_ONCE))
```
```go [Go]
import nolag "github.com/NoLagApp/nolag-go"

// Set default QoS for all messages
client := nolag.New("your_access_token", nolag.Options{
    QoS: nolag.QoSAtLeastOnce,  // Default QoS level
})

client.Connect()

// Override per-message
qos := nolag.QoSExactlyOnce
client.Emit("topic", data, nolag.EmitOptions{QoS: &qos})
```

## Next Steps

- [Learn about Access Control](/docs/concepts/acl)
- [API Reference](/docs/api-reference)
