← blog
Firebase Realtime Database JavaScript SDK v9

Firebase RTDB: one-time read vs real-time listener

Firebase Realtime Database gives you two ways to read data: once (get() / once()) or as a continuous listener (onValue() / on()). Choosing wrong wastes reads and keeps WebSocket connections open for no reason.

SDK v9 (modular) — the modern way

rtdb-v9.js — lettura una tantum
import { getDatabase, ref, get } from 'firebase/database';

const db = getDatabase();

// ✅ Legge una volta sola — ideale per dati storici o config
async function readOnce(path) {
  const snap = await get(ref(db, path));
  if (snap.exists()) {
    return snap.val(); // oggetto JS con i dati
  }
  return null;
}

// Uso
const utente = await readOnce(`users/${uid}`);
console.log(utente.nome);
rtdb-v9.js — ascolto real-time
import { getDatabase, ref, onValue, off } from 'firebase/database';

const db = getDatabase();

// ✅ Ascolta le modifiche in real-time
function listenMessages(chatId, callback) {
  const chatRef = ref(db, `chats/${chatId}/messages`);

  const unsub = onValue(chatRef, snap => {
    const msgs = snap.exists() ? Object.values(snap.val()) : [];
    callback(msgs);
  });

  // Restituisce la funzione di unsubscribe — chiamala quando esci dalla pagina
  return unsub;
}

// Avvia ascolto
const stopListening = listenMessages('room-42', msgs => {
  console.log('Nuovi messaggi:', msgs);
});

// Ferma l'ascolto (es. quando l'utente lascia la pagina)
// stopListening();

SDK v8 (compat) — if you're still on the old SDK

rtdb-v8.js
const db = firebase.database();

// Lettura una tantum (v8)
db.ref(`users/${uid}`).once('value').then(snap => {
  if (snap.exists()) console.log(snap.val());
});

// Ascolto real-time (v8)
const chatRef = db.ref(`chats/room-42/messages`);
chatRef.on('value', snap => {
  console.log(snap.val());
});

// Rimuovi listener
chatRef.off('value');

When to use which

Use caseMethodWhy
User profile on loadget()Static data, doesn't change while user is logged in
App configurationget()Read once, store locally in memory
Real-time chatonValue()Messages arrive while the user is on the page
Online presenceonValue()Changes frequently and unpredictably
Order history / logsget()Immutable past data — no reason to keep listening
Incoming notificationsonValue()Must appear instantly without a page reload

Rule of thumb: if the data can change while the user is on the page and they need to see it immediately, use onValue(). In all other cases use get() and save connections and costs.

Common memory leak: every onValue() opens a WebSocket connection that stays active until you call unsubscribe(). In a SPA, always clean up listeners when the component is unmounted.

Related article
Shared calendar on a PWA: Firebase Realtime Database and FCM push notifications
Related article
Dynamic file catalogue with Firebase RTDB and Storage: admin modal and async race condition

When to use .once() instead of .on() in RTDB

This snippet clarifies the difference between .once() and .on() in the Firebase Realtime Database JavaScript SDK, to help you correctly choose between a single read and continuous real-time listening.

When to use it

When NOT to use it

Alternatives

Common mistakes

Frequently asked questions about single reads vs realtime listening in RTDB

.once() reads the data once and returns a Promise, without continuing to listen for later changes. .on('value', callback) instead keeps listening and calls the callback every time the data changes, until it's removed with .off().

Yes, generally an active .on() listener consumes more bandwidth over time than a single .once() read, because it receives every update to the observed node while it stays active.

You remove it by calling .off() on the same reference and event type used to register it, typically when the component using it unmounts or is no longer needed.