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
| 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
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
- Follow the 5-minute quick start.
- See the full NoLag vs Firebase comparison.
- If you are keeping a database, use NoLag alongside it for the live layer only.