Leggere e scrivere dati sul web

(Facoltativo) Prototipazione e test con Firebase Local Emulator Suite

Prima di parlare di come la tua app legge e scrive in Realtime Database, introduciamo un insieme di strumenti che puoi utilizzare per realizzare prototipi e testare la funzionalità di Realtime Database: Firebase Local Emulator Suite. Se stai provando dati diversi di machine learning, ottimizzando le regole di sicurezza o lavorando per trovare economicamente conveniente per interagire con il backend, potendo lavorare localmente senza il deployment di servizi live può essere un'ottima idea.

Un emulatore Realtime Database fa parte di Local Emulator Suite, che consente alla tua app di interagire con i contenuti e la configurazione del tuo database emulato, nonché facoltativamente le risorse di progetto emulate (funzioni, altri database, e regole di sicurezza).

Per utilizzare l'emulatore Realtime Database sono necessari solo pochi passaggi:

  1. Aggiunta di una riga di codice alla configurazione di test dell'app per connettersi all'emulatore.
  2. Dalla radice della directory del progetto locale, in esecuzione di firebase emulators:start.
  3. Effettuare chiamate dal codice prototipo della tua app utilizzando una piattaforma Realtime Database come di consueto o utilizzando l'API REST Realtime Database.

È disponibile una procedura dettagliata che coinvolge Realtime Database e Cloud Functions. Consulta anche l'introduzione a Local Emulator Suite.

Recuperare un riferimento a un database

Per leggere o scrivere dati dal database, è necessaria un'istanza di firebase.database.Reference:

Web

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web

var database = firebase.database();

Scrittura di dati

Questo documento illustra le nozioni di base sul recupero dei dati e su come ordinarli e filtrare Dati Firebase.

I dati Firebase vengono recuperati collegando un listener asincrono a un firebase.database.Reference. Il listener viene attivato una volta per stato iniziale dei dati e ogni volta che i dati cambiano.

Operazioni di scrittura di base

Per le operazioni di scrittura di base, puoi utilizzare set() per salvare i dati in un sostituendo eventuali dati esistenti in quel percorso. Ad esempio, un social un'applicazione di blogging potrebbe aggiungere un utente con set() nel seguente modo:

Web

import { getDatabase, ref, set } from "firebase/database";

function writeUserData(userId, name, email, imageUrl) {
  const db = getDatabase();
  set(ref(db, 'users/' + userId), {
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

Web

function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

L'utilizzo di set() sovrascrive i dati nella posizione specificata, inclusi eventuali dati secondari nodi.

Lettura di dati

Ascolta gli eventi di valore

Per leggere i dati in un percorso e ascoltare le modifiche, usa onValue() per osservare eventi. Puoi utilizzare questo evento per leggere istantanee statiche dei contenuti in un determinato percorso, così come esistevano al momento dell'evento. Questo metodo viene attivato una volta al momento del collegamento del listener e di nuovo ogni volta che i dati, inclusi gli elementi secondari, vengono modificati. Il callback dell'evento viene trasmesso come snapshot tutti i dati in quella posizione, compresi i dati dei bambini. In assenza di dati, il parametro lo snapshot restituirà false quando chiami exists() e null quando chiami val().

L'esempio seguente mostra un'applicazione di blogging sociale che recupera il numero di stelle di un post dal database:

Web

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const starCountRef = ref(db, 'posts/' + postId + '/starCount');
onValue(starCountRef, (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

Web

var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

L'ascoltatore riceve un snapshot contenente i dati nella posizione specificata nel database al momento dell'evento. Puoi recuperare i dati in snapshot con il metodo val().

Leggi i dati una volta

Leggi i dati una volta con get()

L'SDK è progettato per gestire le interazioni con i server di database, l'app sia online o offline.

In genere, è consigliabile utilizzare le tecniche degli eventi di valore descritte in precedenza per per ricevere notifiche sugli aggiornamenti dei dati dal backend. L'ascoltatore tecniche riducono l'utilizzo e la fatturazione e sono ottimizzate per per offrire agli utenti la migliore esperienza online e offline.

Se hai bisogno dei dati una sola volta, puoi utilizzare get() per ottenere uno snapshot dei dal database. Se per qualsiasi motivo get() non è in grado di restituire il server , il client verifica la cache dell'archiviazione locale e restituisce un errore se non è stato ancora trovato.

L'uso non necessario di get() può aumentare l'uso della larghezza di banda e portare alla perdita di del rendimento, che puoi evitare utilizzando un listener in tempo reale, come mostrato sopra.

Web

import { getDatabase, ref, child, get } from "firebase/database";

const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Web

const dbRef = firebase.database().ref();
dbRef.child("users").child(userId).get().then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

Leggere i dati una volta con un osservatore

In alcuni casi potresti voler restituire il valore della cache locale immediatamente, invece di cercare un valore aggiornato sul server. In quelle casi puoi utilizzare once() per recuperare immediatamente i dati dalla cache del disco locale.

È utile per i dati che devono essere caricati una sola volta e non è previsto che cambiano frequentemente o richiedono un ascolto attivo. Ad esempio, l'app di blogging indicata negli esempi precedenti utilizza questo metodo per caricare il profilo di un utente quando inizia a scrivere un nuovo post:

Web

import { getDatabase, ref, onValue } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const userId = auth.currentUser.uid;
return onValue(ref(db, '/users/' + userId), (snapshot) => {
  const username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
}, {
  onlyOnce: true
});

Web

var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => {
  var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
});

Aggiornamento o eliminazione dei dati

Aggiorna campi specifici

Per scrivere contemporaneamente in nodi secondari specifici di un nodo senza sovrascrivere altri nodi secondari, utilizza il metodo update().

Quando chiami update(), puoi aggiornare i valori secondari di livello inferiore specificando un percorso per la chiave. Se i dati sono archiviati in più località per scalare puoi aggiornare tutte le istanze dei dati utilizzando fan-out dei dati.

Ad esempio, un'app di social blogging potrebbe creare un post e aggiornare contemporaneamente al feed delle attività recenti e a quello dell'utente che ha pubblicato il post utilizzando come questo:

Web

import { getDatabase, ref, child, push, update } from "firebase/database";

function writeNewPost(uid, username, picture, title, body) {
  const db = getDatabase();

  // A post entry.
  const postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  const newPostKey = push(child(ref(db), 'posts')).key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  const updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return update(ref(db), updates);
}

Web

function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  var newPostKey = firebase.database().ref().child('posts').push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase.database().ref().update(updates);
}

Questo esempio utilizza push() per creare un post nel nodo contenente post per tutti gli utenti in /posts/$postid e recuperare contemporaneamente la chiave. La chiave può da utilizzare per creare una seconda voce nella post su /user-posts/$userid/$postid.

Utilizzando questi percorsi, puoi eseguire aggiornamenti simultanei a più località in la struttura JSON con una singola chiamata a update(), come in questo esempio crea il nuovo post in entrambe le posizioni. Gli aggiornamenti simultanei eseguiti in questo modo sono atomici: o tutti gli aggiornamenti vanno a buon fine o tutti non vanno a buon fine.

Aggiungi un callback di completamento

Se vuoi sapere quando è stato eseguito il commit dei dati, puoi aggiungere una di completamento. Sia set() che update() completano un completamento facoltativo che viene chiamato quando viene eseguito il commit della scrittura nel database. Se la chiamata non è andata a buon fine, il callback viene passato che indica il motivo dell'errore.

Web

import { getDatabase, ref, set } from "firebase/database";

const db = getDatabase();
set(ref(db, 'users/' + userId), {
  username: name,
  email: email,
  profile_picture : imageUrl
})
.then(() => {
  // Data saved successfully!
})
.catch((error) => {
  // The write failed...
});

Web

firebase.database().ref('users/' + userId).set({
  username: name,
  email: email,
  profile_picture : imageUrl
}, (error) => {
  if (error) {
    // The write failed...
  } else {
    // Data saved successfully!
  }
});

Elimina dati

Il modo più semplice per eliminare i dati è chiamare remove() su un riferimento alla loro posizione.

Puoi anche eseguire l'eliminazione specificando null come valore per un'altra scrittura come set() o update(). Puoi usare questa tecnica con update() per eliminare più elementi secondari in una singola chiamata API.

Ricevi un Promise

Per sapere quando viene eseguito il commit dei tuoi dati sul server Firebase Realtime Database, puoi usare un Promise Sia set() che update() possono restituire un Promise che puoi usare per sapere quando in scrittura nel database.

Scollega listener

I callback vengono rimossi chiamando il metodo off() sul tuo Riferimento database Firebase.

Puoi rimuovere un singolo listener passandolo come parametro a off(). La chiamata di off() nella località senza argomenti rimuove tutti i listener in quella località in ogni località.

La chiamata a off() per un listener padre non rimuove automaticamente i listener registrati sui relativi nodi figlio; off() deve essere chiamato anche per tutti gli ascoltatori secondari per rimuovere il callback.

Salvare i dati come transazioni

Quando si lavora con dati che potrebbero essere danneggiati da istanze modifiche, come i contatori incrementali, puoi utilizzare operazione di transazione. Puoi assegnare a questa operazione una funzione di aggiornamento e un callback di completamento facoltativo. La funzione di aggiornamento prende lo stato attuale dei dati come un argomento e restituisce il nuovo stato desiderato che vuoi scrivere. Se un altro client scrive nella località prima che il nuovo valore venga inserito correttamente scritto, la funzione di aggiornamento viene richiamata con il nuovo valore corrente e un nuovo tentativo di scrittura.

Ad esempio, nell'esempio di app di social blogging, potresti consentire agli utenti di assegnare e togliere stelle ai post e tenere traccia del numero di stelle ricevute da un post come segue:

Web

import { getDatabase, ref, runTransaction } from "firebase/database";

function toggleStar(uid) {
  const db = getDatabase();
  const postRef = ref(db, '/posts/foo-bar-123');

  runTransaction(postRef, (post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

Web

function toggleStar(postRef, uid) {
  postRef.transaction((post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

L'utilizzo di una transazione impedisce che il conteggio delle stelle sia errato se Gli utenti aggiungono a Speciali lo stesso post nello stesso momento oppure il cliente ha dati non aggiornati. Se viene rifiutata, il server restituisce il valore corrente al client, che esegue nuovamente la transazione con valore aggiornato. L'operazione si ripete finché la transazione non viene accettata o interrotta la transazione.

Incrementi atomici lato server

Nel caso d'uso precedente stiamo scrivendo due valori nel database: l'ID di l'utente che aggiunge/rimuovi da Speciali il post e il numero di stelle incrementato. Se sappiamo già che l'utente aggiunge il post ai preferiti, possiamo utilizzare un'operazione di incremento atomico anziché una transazione.

Web

function addStar(uid, key) {
  import { getDatabase, increment, ref, update } from "firebase/database";
  const dbRef = ref(getDatabase());

  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = increment(1);
  update(dbRef, updates);
}

Web

function addStar(uid, key) {
  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  firebase.database().ref().update(updates);
}

Questo codice non utilizza un'operazione di transazione, quindi non riceve automaticamente esegui nuovamente se è presente un aggiornamento in conflitto. Tuttavia, poiché l'operazione di incremento avviene direttamente sul server del database, non c'è rischio che si verifichino conflitti.

Se vuoi rilevare e rifiutare conflitti specifici delle applicazioni, ad esempio un utente aggiungere a Speciali un post che ha già aggiunto a Speciali, occorre scrivere regole di sicurezza per quel caso d'uso.

Utilizza i dati offline

Se un client perde la connessione di rete, la tua app continua a funzionare in modo corretto.

Ogni client connesso a un database Firebase mantiene la propria versione interna dei dati attivi. Quando i dati vengono scritti, vengono scritti in questa versione locale per prima cosa. Il client Firebase sincronizza quindi i dati con i server database remoto e con altri client in base al criterio "best effort".

Di conseguenza, tutte le scritture nel database attivano immediatamente gli eventi locali, prima che qualsiasi dato venga scritto sul server. Ciò significa che la tua app rimane reattivo, indipendentemente dalla latenza o dalla connettività di rete.

Una volta ristabilita la connettività, la tua app riceve l'insieme appropriato di in modo che il client si sincronizzi con lo stato attuale del server, senza dover scrivere codice personalizzato.

Approfondiremo il comportamento offline nel Scopri di più sulle funzionalità online e offline.

Passaggi successivi