---
title: "Migrate from Ably to NoLag"
description: A practical guide to moving a realtime app from Ably to NoLag, with a concept map and before-and-after code for channels, publishing, presence, and history.
excerpt: "Ably and NoLag are both realtime pub/sub platforms with presence. Migrating is mostly a concept rename. Here is the mapping and the code, side by side."
date: '2026-07-31'
readTime: 6 min read
category: Migration Guide
---

Ably is a mature realtime platform with rich delivery semantics. NoLag shares the same pub/sub foundation and presence model, so moving across is mostly a matter of renaming a few concepts. This guide maps Ably onto NoLag and shows the same flow in both.

## Why teams move

NoLag keeps the realtime transport you rely on and adds:

- **Blueprint SDKs** for chat, notifications, dashboards, tracking, and more.
- **A coordination layer for AI agents**: dispatch, share state, observe, and approve, on the same platform as your app features.
- One place for human-facing and agent-facing realtime.

## Concept map

| Ably | NoLag |
| --- | --- |
| API key / token auth | Actor access tokens |
| Channel | Room + topic |
| `channel.subscribe('event', cb)` | `room.on('topic', cb)` |
| `channel.publish('event', data)` | `room.emit('topic', data)` |
| `channel.presence` | Presence (per room) |
| History | Your own store (NoLag is a messaging layer, not a database) |

## Before: Ably

```js
import * as Ably from 'ably'

const ably = new Ably.Realtime('API_KEY')
const channel = ably.channels.get('chat')

channel.subscribe('message', (msg) => {
  console.log('Received:', msg.data)
})

channel.publish('message', { text: 'Hello!' })
```

## After: NoLag

```ts
import { NoLag } from '@nolag/js-sdk'

// Create the app and its `general` room in the control plane first. The app
// slug you get back has a random suffix (for example `chat-a3f9`): that is
// what you pass to setApp(). Room slugs are kept verbatim.
const APP_SLUG = 'chat-a3f9'

// One actor subscribes...
const receiver = NoLag('receiver_access_token')
await receiver.connect()

const inbox = receiver.setApp(APP_SLUG).setRoom('general')
inbox.subscribe('message')
inbox.on('message', (data) => {
  console.log('Received:', data)
})

// ...and another publishes. Publishers never receive their own messages,
// so a single client that subscribes and emits will not log "Received".
const sender = NoLag('sender_access_token')
await sender.connect()
sender.setApp(APP_SLUG).setRoom('general').emit('message', { text: 'Hello!' })
```

One behavioural difference from Ably is worth flagging before you port a test suite: an Ably client sees its own publishes, a NoLag actor never does. Append the sent message to your UI locally, and use a second actor in tests that expect to receive.

## Presence and history

Ably presence maps to NoLag [presence](/docs/concepts/presence), tracked per room with join and leave events and a live member list. A client sets its presence on the room with `room.setPresence(data)` and listens for `presence:join`, `presence:leave` and `presence:update` on the client.

History is the one Ably feature that does not have a direct counterpart. NoLag is a messaging layer, not a database: a client that reconnects gets its subscriptions restored and nothing replayed, and a fresh subscription receives no history. If you use Ably history to fill gaps after a disconnect or to render the last N messages on load, keep those messages in your own store and read it on connect. The one place the broker does replay is for load-balanced worker groups with persistent sessions, which is a different job; see [Replay and Durable Delivery](/docs/concepts/replay) for exactly what that covers.

## Delivery guarantees

Ably is known for its delivery semantics. NoLag offers [quality-of-service levels](/docs/concepts/qos) 0, 1, and 2 per message, chosen on each `emit` rather than paid for everywhere. Be precise about what they cover: the level governs the hop across NoLag's internal broker, and the WebSocket leg between your client and NoLag has a simpler contract. A publish can be acknowledged (pass a callback to `emit` and it resolves on the broker's `published` ack), nothing is resent for you, and there is no exactly-once guarantee end to end. Put an idempotency key in the payload of anything that must not be applied twice.

## Authentication

Ably authenticates with API keys and tokens, often carrying capabilities in the token itself. NoLag centres permissions on the **actor**: every credential resolves to a typed actor (`user`, `device`, `service`, `session`, `agent`, `orchestrator` or `observer`), and its read and write access lives server-side as per-topic ACL rather than in each token. For browser clients, your backend mints a short-lived JWT ([client token](/docs/client-tokens)) signed with a project signing key, so long-lived credentials never leave your servers.

## Next steps

- Start with the [5-minute quick start](/docs/getting-started).
- Read the deeper [NoLag vs Ably](/compare/ably) comparison.
- Explore the [high-level SDKs](/docs/high-level-sdks) if you would rather not rebuild chat or dashboards by hand.
