Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Leer y escribir datos en la web

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

(Opcional) Realice prototipos y pruebas con Firebase Local Emulator Suite

Antes de hablar sobre cómo su aplicación lee y escribe en Realtime Database, presentemos un conjunto de herramientas que puede usar para crear prototipos y probar la funcionalidad de Realtime Database: Firebase Local Emulator Suite. Si está probando diferentes modelos de datos, optimizando sus reglas de seguridad o trabajando para encontrar la forma más rentable de interactuar con el back-end, poder trabajar localmente sin implementar servicios en vivo puede ser una gran idea.

Un emulador de Realtime Database es parte de Local Emulator Suite, que permite que su aplicación interactúe con el contenido y la configuración de su base de datos emulada, así como, opcionalmente, con los recursos de su proyecto emulado (funciones, otras bases de datos y reglas de seguridad).

Usar el emulador de Realtime Database implica solo unos pocos pasos:

  1. Agregar una línea de código a la configuración de prueba de su aplicación para conectarse al emulador.
  2. Desde la raíz del directorio de tu proyecto local, ejecuta firebase emulators:start .
  3. Hacer llamadas desde el código prototipo de su aplicación usando un SDK de la plataforma Realtime Database como de costumbre, o usando la API REST de Realtime Database.

Se encuentra disponible un tutorial detallado que involucra Realtime Database y Cloud Functions . También debería echar un vistazo a la introducción de Local Emulator Suite .

Obtener una referencia de base de datos

Para leer o escribir datos de la base de datos, necesita una instancia de firebase.database.Reference :

Web version 9

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web version 8

var database = firebase.database();

Escribir datos

Este documento cubre los aspectos básicos de la recuperación de datos y cómo ordenar y filtrar datos de Firebase.

Los datos de Firebase se recuperan adjuntando un oyente asincrónico a firebase.database.Reference . El detector se activa una vez para el estado inicial de los datos y nuevamente cada vez que los datos cambian.

Operaciones básicas de escritura

Para operaciones básicas de escritura, puede usar set() para guardar datos en una referencia específica, reemplazando cualquier dato existente en esa ruta. Por ejemplo, una aplicación de blogs sociales podría agregar un usuario con set() de la siguiente manera:

Web version 9

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 version 8

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

El uso set() sobrescribe los datos en la ubicación especificada, incluidos los nodos secundarios.

Leer datos

Escuche los eventos de valor

Para leer datos en una ruta y escuchar cambios, use onValue() para observar eventos. Puede usar este evento para leer instantáneas estáticas de los contenidos en una ruta determinada, tal como existían en el momento del evento. Este método se activa una vez cuando se adjunta el oyente y nuevamente cada vez que cambian los datos, incluidos los elementos secundarios. La devolución de llamada del evento recibe una instantánea que contiene todos los datos en esa ubicación, incluidos los datos secundarios. Si no hay datos, la instantánea devolverá false cuando llame a exists() y null cuando llame a val() en ella.

El siguiente ejemplo muestra una aplicación de blogs sociales que recupera el recuento de estrellas de una publicación de la base de datos:

Web version 9

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 version 8

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

El oyente recibe una snapshot que contiene los datos en la ubicación especificada en la base de datos en el momento del evento. Puede recuperar los datos en la snapshot con el método val() .

Leer datos una vez

Leer datos una vez con get()

El SDK está diseñado para administrar las interacciones con los servidores de la base de datos, ya sea que su aplicación esté en línea o fuera de línea.

En general, debe usar las técnicas de eventos de valor descritas anteriormente para leer datos y recibir notificaciones de actualizaciones de datos desde el backend. Las técnicas de escucha reducen su uso y facturación, y están optimizadas para brindarles a sus usuarios la mejor experiencia mientras se conectan y desconectan.

Si necesita los datos solo una vez, puede usar get() para obtener una instantánea de los datos de la base de datos. Si por alguna razón get() no puede devolver el valor del servidor, el cliente sondeará el caché de almacenamiento local y devolverá un error si aún no se encuentra el valor.

El uso innecesario de get() puede aumentar el uso del ancho de banda y provocar una pérdida de rendimiento, lo que se puede evitar mediante el uso de un oyente en tiempo real como se muestra arriba.

Web version 9

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 version 8

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);
});

Leer datos una vez con un observador

En algunos casos, es posible que desee que el valor de la memoria caché local se devuelva inmediatamente, en lugar de buscar un valor actualizado en el servidor. En esos casos, puede usar once() para obtener los datos del caché del disco local de inmediato.

Esto es útil para los datos que solo deben cargarse una vez y no se espera que cambien con frecuencia ni requieran una escucha activa. Por ejemplo, la aplicación de blogs de los ejemplos anteriores usa este método para cargar el perfil de un usuario cuando comienza a crear una nueva publicación:

Web version 9

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 version 8

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

Actualizar o eliminar datos

Actualizar campos específicos

Para escribir simultáneamente en elementos secundarios específicos de un nodo sin sobrescribir otros nodos secundarios, utilice el método update() .

Al llamar a update() , puede actualizar los valores secundarios de nivel inferior especificando una ruta para la clave. Si los datos se almacenan en varias ubicaciones para escalar mejor, puede actualizar todas las instancias de esos datos mediante distribución de datos .

Por ejemplo, una aplicación de blogs sociales podría crear una publicación y actualizarla simultáneamente con la fuente de actividad reciente y la fuente de actividad del usuario que publica la publicación usando un código como este:

Web version 9

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 version 8

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);
}

Este ejemplo usa push() para crear una publicación en el nodo que contiene publicaciones para todos los usuarios en /posts/$postid y recuperar la clave simultáneamente. Luego, la clave se puede usar para crear una segunda entrada en las publicaciones del usuario en /user-posts/$userid/$postid .

Con estas rutas, puede realizar actualizaciones simultáneas en varias ubicaciones en el árbol JSON con una sola llamada a update() , como en este ejemplo, se crea la nueva publicación en ambas ubicaciones. Las actualizaciones simultáneas realizadas de esta manera son atómicas: todas las actualizaciones se realizan correctamente o todas fallan.

Agregar una devolución de llamada de finalización

Si desea saber cuándo se confirmaron sus datos, puede agregar una devolución de llamada de finalización. Tanto set() como update() toman una devolución de llamada de finalización opcional que se llama cuando la escritura se ha confirmado en la base de datos. Si la llamada no tuvo éxito, la devolución de llamada recibe un objeto de error que indica por qué ocurrió la falla.

Web version 9

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 version 8

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

Borrar datos

La forma más sencilla de eliminar datos es llamar a remove() en una referencia a la ubicación de esos datos.

También puede eliminar especificando null como valor para otra operación de escritura, como set() o update() . Puede usar esta técnica con update() para eliminar varios elementos secundarios en una sola llamada a la API.

Recibe una Promise

Para saber cuándo se confirman sus datos en el servidor de base de datos en tiempo real de Firebase, puede usar una Promise . Tanto set() como update() pueden devolver una Promise que puede usar para saber cuándo se confirma la escritura en la base de datos.

Separar oyentes

Las devoluciones de llamada se eliminan llamando al método off() en la referencia de la base de datos de Firebase.

Puede eliminar un único oyente pasándolo como parámetro a off() . Calling off() en la ubicación sin argumentos elimina todos los oyentes en esa ubicación.

Calling off() en un agente de escucha principal no elimina automáticamente los agentes de escucha registrados en sus nodos secundarios; off() también debe llamarse en cualquier oyente secundario para eliminar la devolución de llamada.

Guardar datos como transacciones

Cuando trabaje con datos que podrían corromperse por modificaciones simultáneas, como contadores incrementales, puede usar una operación de transacción . Puede otorgar a esta operación una función de actualización y una devolución de llamada de finalización opcional. La función de actualización toma el estado actual de los datos como argumento y devuelve el nuevo estado deseado que le gustaría escribir. Si otro cliente escribe en la ubicación antes de que su nuevo valor se escriba correctamente, su función de actualización se vuelve a llamar con el nuevo valor actual y se vuelve a intentar la escritura.

Por ejemplo, en la aplicación de blogs sociales de ejemplo, puede permitir que los usuarios destaquen y no destaquen publicaciones y realicen un seguimiento de cuántas estrellas ha recibido una publicación de la siguiente manera:

Web version 9

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 version 8

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;
  });
}

El uso de una transacción evita que los recuentos de estrellas sean incorrectos si varios usuarios protagonizan la misma publicación al mismo tiempo o si el cliente tenía datos obsoletos. Si se rechaza la transacción, el servidor devuelve el valor actual al cliente, que vuelve a ejecutar la transacción con el valor actualizado. Esto se repite hasta que se acepta la transacción o se cancela la transacción.

Incrementos atómicos del lado del servidor

En el caso de uso anterior, estamos escribiendo dos valores en la base de datos: la ID del usuario que destaca/quita la estrella de la publicación y el número de estrellas incrementado. Si ya sabemos que el usuario está protagonizando la publicación, podemos usar una operación de incremento atómico en lugar de una transacción.

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);
}

Este código no utiliza una operación de transacción, por lo que no se vuelve a ejecutar automáticamente si hay una actualización conflictiva. Sin embargo, dado que la operación de incremento ocurre directamente en el servidor de la base de datos, no hay posibilidad de conflicto.

Si desea detectar y rechazar conflictos específicos de la aplicación, como un usuario que destaca una publicación que ya ha destacado anteriormente, debe escribir reglas de seguridad personalizadas para ese caso de uso.

Trabajar con datos sin conexión

Si un cliente pierde su conexión de red, su aplicación seguirá funcionando correctamente.

Cada cliente conectado a una base de datos de Firebase mantiene su propia versión interna de los datos activos. Cuando se escriben datos, primero se escriben en esta versión local. Luego, el cliente de Firebase sincroniza esos datos con los servidores de base de datos remotos y con otros clientes en base al "mejor esfuerzo".

Como resultado, todas las escrituras en la base de datos desencadenan eventos locales inmediatamente, antes de que se escriba ningún dato en el servidor. Esto significa que su aplicación sigue respondiendo independientemente de la latencia o la conectividad de la red.

Una vez que se restablece la conectividad, su aplicación recibe el conjunto de eventos adecuado para que el cliente se sincronice con el estado actual del servidor, sin tener que escribir ningún código personalizado.

Hablaremos más sobre el comportamiento fuera de línea en Obtenga más información sobre las capacidades en línea y fuera de línea .

Próximos pasos