---
title: "Migrate from Pusher to NoLag"
description: A practical guide to moving a realtime app from Pusher Channels to NoLag, with a concept map and before-and-after code for subscribing and publishing.
excerpt: "Pusher and NoLag share the same pub/sub shape, so most migrations are a rename of a few concepts. Here is the mapping and the code, side by side."
date: '2026-07-29'
readTime: 6 min read
category: Migration Guide
---

Pusher Channels popularised hosted realtime pub/sub, and a lot of apps still run on it. If you are moving to NoLag, the good news is that the mental model is almost identical: you subscribe to a channel, you bind to events, and a publisher sends data to everyone listening. This guide maps Pusher concepts onto NoLag and shows the same subscribe-and-publish flow in both.

## Why teams move

NoLag keeps the pub/sub foundation you already rely on and adds a few things Pusher does not focus on:

- **Blueprint SDKs** for chat, notifications, dashboards, tracking, and more, so common patterns are a few lines instead of a from-scratch build.
- **A coordination layer for AI agents**: dispatch work, share state, observe decisions, and gate actions with human approval, on the same infrastructure as your app.
- **Quality-of-service levels** and presence built in.

## Concept map

| Pusher | NoLag |
| --- | --- |
| App key + cluster | App (a container for rooms and topics) |
| Channel | Room + topic |
| `channel.bind('event', cb)` | `room.on('topic', cb)` |
| `pusher.trigger('channel', 'event', data)` | `room.emit('topic', data)` |
| Presence channel | Presence (per room) |
| Private channel + auth endpoint | Actors, access tokens, and per-topic ACL |

## Before: Pusher

```js
import Pusher from 'pusher-js'

const pusher = new Pusher('APP_KEY', { cluster: 'eu' })

const channel = pusher.subscribe('chat')
channel.bind('message', (data) => {
  console.log('Received:', data)
})

// On your server, using the pusher server SDK:
// pusher.trigger('chat', 'message', { text: 'Hello!' })
```

## After: NoLag

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

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

const room = client.setApp('chat').setRoom('general')
room.subscribe('message')

room.on('message', (data) => {
  console.log('Received:', data)
})

// Publish from any authorised client:
room.emit('message', { text: 'Hello!' })
```

The shape is the same. The main differences are that NoLag groups topics inside a **room** (so you get natural namespacing and presence per room), and access is controlled with **actors and tokens** rather than a per-channel auth endpoint.

## Auth and access control

In Pusher, private and presence channels call back to an auth endpoint you host. In NoLag, a connection authenticates once with an **actor access token**, and what that actor can read or write is enforced at the broker with per-topic ACL. For browser clients, you can mint short-lived [client tokens](/docs/agents) from your project signing keys so you never ship a long-lived secret.

## Presence

Pusher presence channels map to NoLag [presence](/docs/concepts/presence), tracked per room. You get join and leave events and the current member list without standing up your own tracking.

## Next steps

- Follow the [5-minute quick start](/docs/getting-started) to connect for the first time.
- If you are building chat, the [chat blueprint](/docs/high-level-sdks) gives you rooms, typing, and history out of the box.
- Compare the platforms in more detail: [NoLag vs Pusher](/compare/pusher).
