---
title: "Migrate from Firebase to NoLag"
description: How to move the realtime layer of a Firebase app to NoLag, when it makes sense, and what to keep. Includes a concept map and before-and-after code.
excerpt: "Firebase syncs data through database listeners; NoLag is explicit pub/sub. Here is how to migrate the realtime layer, and the one thing you should not try to move."
date: '2026-08-01'
readTime: 7 min read
category: Migration Guide
---

Firebase Realtime Database and Firestore give you realtime by syncing stored documents and firing client listeners when the data changes. NoLag takes a different approach: explicit publish and subscribe over topics. That difference matters for migration, so let us be clear up front about what to move and what to keep.

## What to migrate, and what not to

NoLag is a **messaging layer, not a database**. If you use Firebase purely as a realtime signal, presence, or message bus, NoLag replaces that cleanly and gives you more control over delivery. If you use Firebase as your **system of record**, keep a database. Migrate the realtime and pub/sub usage to NoLag, and let your data live wherever suits you. Many teams end up with NoLag for the live layer and a database of their choice behind it.

This also answers the question every Firebase team asks first: "what does a client see when it reconnects?" In Firebase, the current document. In NoLag, its subscriptions are restored and it receives what is published from then on; nothing is replayed and a fresh subscription gets no history. The database you keep is where "current state" lives, and the client reads it on connect. (The broker does replay for load-balanced worker groups with persistent sessions, which is a different job; see [Replay and Durable Delivery](/docs/concepts/replay).)

## Why teams move the realtime layer

- **Explicit pub/sub** instead of modelling every realtime feature as a change on a stored document.
- **Database-agnostic**: add realtime to any stack rather than standardising on Firestore.
- **Blueprint SDKs** for chat, notifications, dashboards, and tracking.
- **A coordination layer for AI agents** on the same platform.

## Concept map

| Firebase | NoLag |
| --- | --- |
| Realtime Database ref / Firestore collection | Room + topic |
| `onValue` / `onSnapshot` listener | `room.on('topic', cb)` |
| `set` / `update` / `push` | `room.emit('topic', data)` |
| Security rules | Actors, access tokens, per-topic ACL |
| Presence via connection state | Presence (per room) |

## Before: Firebase Realtime Database

```js
import { getDatabase, ref, onValue, push } from 'firebase/database'

const db = getDatabase()
const messagesRef = ref(db, 'chat/general/messages')

onValue(messagesRef, (snapshot) => {
  console.log('Data changed:', snapshot.val())
})

push(messagesRef, { text: 'Hello!', sender: 'user-123' })
```

## 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 listens...
const receiver = NoLag('receiver_access_token')
await receiver.connect()

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

// ...and another publishes. Publishers never receive their own messages,
// so the sender appends its own message to the UI locally.
const sender = NoLag('sender_access_token')
await sender.connect()
sender.setApp(APP_SLUG).setRoom('general').emit('messages', { text: 'Hello!', sender: 'user-123' })
```

The key shift is from "write to a path and listen for changes" to "publish an event and subscribe to it." You send exactly the message you mean, rather than reshaping your data model so that a write triggers the right listeners. The other shift is that a Firebase listener fires for the writer too, and a NoLag publisher never receives its own message: update your local state when you send, and let the subscription carry everyone else's.

## Access control

Firebase security rules become **per-topic ACL** in NoLag, tied to a typed **actor** (`user`, `device`, `service`, `session`, `agent`, `orchestrator` or `observer`) and enforced at the broker rather than written in a rules language. A connection authenticates with an actor token, and browser clients use short-lived JWTs ([client tokens](/docs/client-tokens)) your backend signs with a project signing key.

## Next steps

- Follow the [5-minute quick start](/docs/getting-started).
- See the full [NoLag vs Firebase](/compare/firebase) comparison.
- If you are keeping a database, use NoLag alongside it for the live layer only.
