← Back to blog
MIGRATION GUIDE7 min read

Migrate from Firebase to NoLag

HB
Henco Burger
August 1, 2026

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.)

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

FirebaseNoLag
Realtime Database ref / Firestore collectionRoom + topic
onValue / onSnapshot listenerroom.on('topic', cb)
set / update / pushroom.emit('topic', data)
Security rulesActors, access tokens, per-topic ACL
Presence via connection statePresence (per room)

Before: Firebase Realtime Database

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

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) your backend signs with a project signing key.

Next steps